Install
The runner and its panel, on your own machine.
Self-hosted Zeroko is two containers: the runner, which runs the services, and the panel, to manage it and to give agents the skill and the API. Or one: the panel with the runner inside. The images are for amd64 and arm64:
| Image | |
|---|---|
ghcr.io/zeroko-io/runner | The runner. AGPL. |
ghcr.io/zeroko-io/panel | The panel. MIT. |
ghcr.io/zeroko-io/panel:<tag>-aio | Both in one container ("all in one"). |
Tags: X.Y.Z, X.Y and latest for releases; dev follows the main
branch. Each panel tag has its all-in-one twin: X.Y.Z-aio, X.Y-aio,
latest-aio, dev-aio.
Start
The panel's repository has a docker-compose.yml that starts both, and an
.env.example:
cp .env.example .env # set the token and the password
docker compose up -d| Variable | |
|---|---|
ZEROKO_TOKEN | The runner's root token, 32 characters or more. The panel uses the same one. |
ZEROKO_PANEL_PASSWORD | The admin's password, 8 characters or more. |
ZEROKO_PANEL_URL | The panel's public address, when its proxy doesn't send X-Forwarded-Host and X-Forwarded-Proto. |
PANEL_PORT, GATEWAY_PORT | The ports on the host: 3000 for the panel, 8080 for services. |
RUNNER_TAG, PANEL_TAG | The images' tags. |
Changing the token or the password signs the admin out.
Or in one container
The all-in-one image is the same panel with the runner inside: the
runner's newest release when the image was built. One container, one
volume for the runner's data (/data), and only a password to set: the
root token is made on the first start and kept in that volume. The
panel's repository has a docker-compose-aio.yml for it, and an
.env.aio.example:
cp .env.aio.example .env # set the password
docker compose -f docker-compose-aio.yml up -d| Variable | |
|---|---|
ZEROKO_PANEL_PASSWORD | The admin's password, 8 characters or more. |
ZEROKO_PANEL_URL | The panel's public address, as above. |
ZEROKO_TOKEN | Optional: a root token of your own, 32 characters or more. Without it, one is made and kept in /data. |
PANEL_TAG | The tag, without -aio: latest, dev or a version. |
PANEL_PORT, GATEWAY_PORT | The ports on the host: 3000 for the panel, 8080 for services. |
Or without compose:
docker run -d --name zeroko --restart unless-stopped \
-p 3000:3000 -p 8080:8080 -v zeroko:/data \
-e ZEROKO_PANEL_PASSWORD=… ghcr.io/zeroko-io/panel:latest-aioInside, the runner and the panel run side by side: the runner as
ZEROKO_UID:ZEROKO_GID (1000 by default), the panel as the image's
node user. If either stops, the container stops, and Docker's restart
policy starts both again. The runner's API (8081) stays inside the
container. The root token is never a fixed one, even so: agents reach the
runner's API through the panel's /api, where the root token would work
too. The rest of this page applies as it is: the proxy, the domain,
backups. A newer runner comes with a newer image: pull it to upgrade both.
Put a proxy with TLS in front
The panel answers on port 3000 and services on port 8080, both over plain HTTP. Put a reverse proxy with TLS in front of both, whichever you use:
- one host for the panel, say
panel.example.com, to port 3000; - a wildcard for the services,
*.example.com, to port 8080, with a wildcard certificate or one per service.
The proxy must allow long responses and pass WebSocket upgrades, for realtime connections.
Set the domain
Sign in to the panel and set the domain in System: services are served at
<name>.<domain>, and none is until it is set. That restarts the runner in
place.
The runner's API (port 8081) stays inside the compose network: agents reach
it through the panel, at https://panel.example.com/api, with a token made in
Tokens.
Next
- The panel: what it does, and its settings.
- Runner settings: limits and where data lives.
- Backups and upgrades.
- Security: what isolates what, and what doesn't.