A small, self-hosted, mobile-first web UI for updating your docker compose
containers (works great with Dockge). It
checks your images' registries for newer versions and lets you apply updates
with one tap — manually, never automatically. No watchtower-style surprise
upgrades, and no Diun or external notifier required.
One screen lists your containers grouped by stack, shows which have updates, and updates them with a tap.
Drop this into a compose file on your Docker host and start it:
services:
dockpull:
image: ghcr.io/strandedturtle/dockpull:latest
container_name: dockpull
restart: unless-stopped
ports:
- "5000:5000"
environment:
- ADMIN_PASSWORD=change-me # your login password
- SESSION_SECRET=REPLACE_ME # run: openssl rand -hex 32
- STACKS_DIR=/opt/stacks # absolute host path to your stacks
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /opt/stacks:/opt/stacks # ⚠️ SAME path on host AND container (see below)
- dockpull-data:/data
volumes:
dockpull-data:docker compose up -dThen open http://<host-ip>:5000 and log in with your ADMIN_PASSWORD. That's it —
the dashboard runs an update check automatically on first load.
Building from source instead?
git clonethis repo,cp .env.example .env, fill in the three values above, and rundocker compose up -d --build.
Your stacks directory must be bind-mounted at the same absolute path on the host
and inside the container, and STACKS_DIR must equal that path:
- /opt/stacks:/opt/stacks # host path : identical container pathWhy: DockPull runs docker compose against the host daemon, but the compose CLI
reads the compose file from this container's filesystem, and the daemon resolves
relative paths (./data:/data, build contexts, env_file) against the host path.
If the paths don't match you'll get compose file not found and broken bind mounts.
(Dockge imposes the same rule, for the same reason.) Dockge's default is
/opt/stacks. DockPull shows a banner if it detects the mount is wrong.
- Updates tab — containers grouped by stack, update-available ones on top. Defaults to showing only what needs updating; flip to All to see everything. Tap Update to pull + recreate that service (watch live logs), or Update all to run them one at a time. After an update DockPull verifies the container actually comes up healthy (catching crash-loops), and offers a one-click Revert to the previous image if it doesn't. Pin Version holds a container at its current version.
- History tab — a log of past updates. Clear history wipes it (with a confirm).
- Settings tab — theme, default view, auto-check on open, the daily background scan + notifications (Discord, ntfy, Gotify, or a generic webhook — with a "send test" button), and pinned-version management.
- Install as an app (PWA) — use your browser's "Add to Home Screen" / "Install" to get a standalone, full-screen icon.
The update check queries registries directly (Docker Hub, GHCR, lscr.io, quay.io, …). Public images work anonymously. For private images (and to dodge Docker Hub's anonymous rate limit), mount your Docker credentials read-only so DockPull can authenticate:
volumes:
- ~/.docker/config.json:/root/.docker/config.json:roThis is the file docker login writes; only static auths entries are used (not
credential-helper stores).
All config is via environment variables (see .env.example).
| Var | Default | Required | Notes |
|---|---|---|---|
ADMIN_PASSWORD |
— | ✅ | Single shared login password. |
SESSION_SECRET |
— | ✅ | Signs the session cookie. openssl rand -hex 32. |
STACKS_DIR |
/stacks |
✅ | Host path of your stacks; mount it at the identical path. |
PORT |
5000 |
Server listen port. | |
DATA_DIR |
/data |
SQLite location; persist via a volume. | |
SESSION_TTL |
604800 |
Login cookie lifetime in seconds (7 days). | |
BASE_URL |
http://localhost:5000 |
Public URL; if https, the cookie is set Secure. |
|
TRUST_PROXY |
off | Set (e.g. 1) when behind a reverse proxy so rate-limiting sees real client IPs. |
|
DISCORD_WEBHOOK_URL |
— | Discord webhook for notifications (also editable in Settings). | |
GITHUB_TOKEN |
— | Optional read-only token; raises GitHub's 60/hr changelog/version API limit to 5000/hr. | |
SCHEDULED_CHECK_TIME |
09:00 |
Daily local time (HH:MM) for the background scan. | |
TZ |
UTC |
Container timezone the scan clock uses (e.g. Europe/London); UTC by default. |
|
BACKGROUND_CHECK_ENABLED |
true |
Whether the scheduled scan runs. | |
SELF_CONTAINER_NAME |
dockpull |
This app's container, excluded so it can't update itself. |
The two required vars are enforced at startup — the server won't boot without them.
DockPull mounts the Docker socket, which is root-equivalent on the host. Run it
on a trusted network behind its login — don't expose it raw to the internet. It
ships with a constant-time password check, per-IP login lockout, SSRF-guarded
webhooks, and security headers. See SECURITY.md for the threat
model and hardening tips (HTTPS/BASE_URL, TRUST_PROXY, SESSION_TTL).
To update DockPull itself: docker compose pull dockpull && docker compose up -d dockpull.
Behind a reverse proxy? Set TRUST_PROXY=1. To serve under a subpath
(https://host/dockpull/), either have the proxy strip the prefix, or set
BASE_PATH=/dockpull and build the client with the same value
(BASE_PATH=/dockpull docker compose build).
Behind a Cloudflare Tunnel? A tunnel makes DockPull reachable from the
public internet, and DockPull controls your Docker socket — the built-in
password alone is not enough there. Put a Cloudflare Access
policy (Zero Trust login) in front of the hostname, and set TRUST_PROXY=1
and BASE_URL=https://your-hostname so rate-limiting sees real client IPs and
the session cookie is marked Secure.
compose file not foundon update → the same-path mount.STACKS_DIRmust match the host path and the container mount path.503 docker_unavailable→ the app can't reach the Docker daemon; check the socket is mounted andDOCKER_SOCKETmatches.- Some images never flag / show under
errors→ they're private and need credentials; the anonymous check can't query them. - Can't log in / cookie not sticking → on HTTPS, set
BASE_URLto yourhttps://URL so theSecurecookie is issued; clear the cookie and retry. - Image tags:
:latesttracksmain; version tags (:1.0.0,:1.0) are cut per release for pinning. If a host can't pull, the GHCR package may be private — make it public ordocker login ghcr.io.
DISCLAIMER: This project was made using Claude
Endpoint/field reference: API_CONTRACT.md ·
Development setup: CONTRIBUTING.md · License: MIT.