Skip to content

Installation

Kaleb Fenley edited this page Jul 29, 2026 · 1 revision

Installation

Requirements

  • Python 3.10+ — the codebase uses PEP 604 (X | None) annotations that are evaluated at runtime, so 3.9 and earlier fail at import.
  • ISC Kea (optional) — kea-dhcp4 and/or kea-dhcp6. Without it, EZ-Kea runs in DEMO mode against a local sandbox.

EZ-Kea serves plain HTTP through waitress. It does not terminate TLS itself.

From source

git clone https://github.com/fenleytech/ez-kea.git
cd ez-kea
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
python3 app.py

Open http://127.0.0.1:8080 and sign in with admin / changeme. Both the username and password must be changed at first login.

Binding to a network address

EZ-Kea binds 127.0.0.1:8080 by default. Two environment variables change that, and they interact:

export HOST=0.0.0.0
export PORT=8080
export SECRET_KEY="$(python3 -c 'import secrets;print(secrets.token_hex(32))')"
python3 app.py

EZ-Kea refuses to start on a non-loopback HOST while SECRET_KEY is still the default dev. This is deliberate: the secret key signs session cookies and CSRF tokens, and a predictable one on a reachable address is worth refusing to boot over. On loopback it's a loud warning instead of a hard stop.

Running as a systemd service

# /etc/systemd/system/ez-kea.service
[Unit]
Description=EZ-Kea
After=network.target

[Service]
Type=simple
User=ezkea
Group=ezkea
WorkingDirectory=/opt/ez-kea

Environment=HOST=127.0.0.1
Environment=PORT=8080
EnvironmentFile=/etc/ez-kea.env

ExecStart=/opt/ez-kea/venv/bin/python /opt/ez-kea/app.py
Restart=always
RestartSec=2

NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Create the environment file holding the secret key separately, so it isn't world-readable in the unit:

sudo install -o ezkea -g ezkea -m 0600 /dev/null /etc/ez-kea.env
echo "SECRET_KEY=$(python3 -c 'import secrets;print(secrets.token_hex(32))')" \
    | sudo tee -a /etc/ez-kea.env
sudo systemctl daemon-reload
sudo systemctl enable --now ez-kea

Watch out: the unit above binds loopback. If you want EZ-Kea reachable from the rest of your network, either put a reverse proxy in front of it (recommended) or set Environment=HOST=0.0.0.0 — and note that this only works once SECRET_KEY is set, per the refusal described above.

File permissions

The service account needs to read and write:

  • the Kea config file(s) it manages — typically /etc/kea/kea-dhcp4.conf
  • the backup directory (BACKUP_DIR)
  • its own data directory (settings JSON and SQLite database)

and read:

  • the Kea lease CSVs (/var/lib/kea/) and logs (/var/log/kea/)

It also needs permission to run your reload command — keactrl reload usually means sudo rights or membership in an appropriate group.

Behind a reverse proxy

Bind EZ-Kea to loopback and terminate TLS at the proxy:

server {
    listen 443 ssl;
    server_name kea.example.com;

    ssl_certificate     /etc/ssl/certs/kea.example.com.pem;
    ssl_certificate_key /etc/ssl/private/kea.example.com.key;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Kea running in Docker

If Kea runs in a container on the same host, point EZ-Kea's Kea commands at docker exec:

KEA_DHCP4_CMD="docker exec my-kea kea-dhcp4"
KEA_CTRL_CMD="docker exec my-kea keactrl"

That alone isn't enough. docker exec ... kea-dhcp4 -t <path> runs inside the container's filesystem namespace, where the host path EZ-Kea reads and writes usually doesn't exist — the volume mount exposes the same file somewhere else. Tell EZ-Kea both paths:

DHCP_CONFIG_FILE=/srv/kea/kea-dhcp4.conf              # host path EZ-Kea edits
DHCP_CONFIG_FILE_IN_CONTAINER=/etc/kea/kea-dhcp4.conf # path to pass to the binary
DHCP_LOG_FILE=/srv/kea/kea-dhcp4.log
DHCP_LOG_FILE_IN_CONTAINER=/var/log/kea-dhcp4.log

Minimal Kea images often ship without /etc/kea/keactrl.conf, which keactrl requires in order to run at all — so keactrl reload fails even once the paths are right. Use the SIGHUP reload strategy instead:

KEA_RELOAD_STRATEGY=sighup
KEA_DOCKER_CONTAINER=my-kea

which runs docker kill -s HUP my-kea. This is never inferred automatically: guessing wrong about how to reload a production DHCP server is worse than asking for one more setting.

All of these are also editable in the UI under Global Settings → Docker Deployment, and a worked example lives in Testbed.

Clone this wiki locally