Documentation

Everything you need to self-host.

PullPilot manages updates for the Docker images and services in your homelab — status, logs and deployment modes — from a single UI. Here's how to get it running and tuned.

Quick install

No .env is required if you use the default stacks path /srv/docker-stacks. The official compose sets ALLOW_NO_AUTH=true so the quick start works on a trusted LAN.

bash
$ sudo mkdir -p /srv/docker-stacks
$ mkdir -p ~/pullpilot && cd ~/pullpilot
$ curl -fsSL -o docker-compose.yml https://raw.githubusercontent.com/KN990x/PullPilot/main/docker-compose.yml
$ curl -fsSL -o .env.example https://raw.githubusercontent.com/KN990x/PullPilot/main/.env.example
$ docker compose up -d

Then open http://your-server-ip:8000 (or the host port from PULLPILOT_PORT).

After startup

Each project is a subfolder under your stacks root with a docker-compose.yml (or docker-compose.yaml) inside. Keep PullPilot's own compose folder outside that tree when you can.

/srv/docker-stacks/          # default DOCKER_ROOT_PATH
├── plex/
   └── docker-compose.yml
├── uptime-kuma/
   └── docker-compose.yml
└── ...

Folders named pullpilot, pullpilot-ui, docker-updater and data are ignored under the stacks root.

Using a different stacks location

Create the folder on the host, then add an .env next to docker-compose.yml pointing at it. The path must be identical on the host and inside the container, because it is bind-mounted one-to-one. After any .env change, run docker compose up -d (or docker compose restart).

.env
DOCKER_ROOT_PATH=/absolute/path/to/stacks

Usage guide

Dashboard

A card per project — status, per-project update, and Full stop and Exclude toggles.

Update All

Scans non-excluded projects, runs git pull where applicable, recreates containers; summary in History.

Schedule

Default global update daily at 04:00 (container time), or define your own per project.

Environment variables

A single list for your Compose .env. Copy .env.example and override only what you need.

VariableDefaultDescription
DOCKER_ROOT_PATH/srv/docker-stacksSame absolute stacks path on host and container (bind mount).
PROJECTS_ROOT(from DOCKER_ROOT_PATH)Advanced: use only when the path inside the container must differ from the bind mount.
PULLPILOT_PORT8000Published host port for the UI.
TZUTCContainer timezone.
DATA_DIR/app/dataSQLite and runtime data (the official compose uses the pullpilot_data volume).
ALLOW_NO_AUTHfalse**The official compose defaults to true. If false, AUTH_USER and AUTH_PASS are required or startup fails.
AUTH_USER / AUTH_PASSRequired when ALLOW_NO_AUTH=false. If both are set, session login is enforced.
SESSION_SECRET(generated)Fixed value ⇒ sessions survive restarts. Required if UVICORN_WORKERS > 1. Generate one with openssl rand -hex 32.
SESSION_HTTPS_ONLYfalseSet true if the app is only served over HTTPS.
SESSION_SAME_SITElaxCookie SameSite: lax, strict or none. Use none only over HTTPS — browsers require Secure.
CORS_ORIGINS(empty)Comma-separated origins. Empty is usually fine when the SPA is served by the same app.
HEALTHCHECK_TIMEOUT60Post-deploy health wait, in seconds.
COMMAND_TIMEOUT300External command timeout, in seconds.
LOG_LOCALEesLanguage for scheduled update logs and history (es or en). UI-triggered updates follow Accept-Language instead.
LOGIN_RATE_LIMIT_ENABLEDtrueIn-memory login rate limit, per IP.
LOGIN_RATE_LIMIT_MAX15Max login attempts per window.
LOGIN_RATE_LIMIT_WINDOW_SEC300Rate limit window length, in seconds.
TRUST_X_FORWARDED_FORfalseUse X-Forwarded-For for the login rate limit. Enable only behind a proxy you trust.

Security notes

Treat PullPilot like root access — the Docker socket is mounted. Never expose port 8000 to the public internet without TLS, ALLOW_NO_AUTH=false, strong credentials, and ideally an extra auth layer (Authelia, Authentik…).

Updates and scheduled jobs only run under the resolved stacks root. Database paths outside that tree are rejected.

Behind a TLS-terminating proxy, set SESSION_HTTPS_ONLY=true, and set a fixed SESSION_SECRET so sessions survive restarts.

Run one Uvicorn worker per instance — the scheduler and the login rate limit are in-memory. If you ever set UVICORN_WORKERS above 1, you must set SESSION_SECRET so every worker shares the same signing key.

The published image is ghcr.io/kn990x/pullpilot. If you still pin ghcr.io/kernel-nomad/pullpilot, update your compose file — that path is no longer maintained.