Self-hosted Docker Compose stack management UI. Browse, start, stop, restart, and redeploy your stacks from a web interface — no big database, no complex config.
- Docker Compose first — stacks are plain directories with a
compose.yaml(and.env), managed on disk - Fast — built on Bun + TanStack Start
- Stateless and local-first — SQLite used only for auth; stack state lives in Docker itself and on your filesystem
- Built-in editor — edit compose files directly in the UI with YAML validation
- Webhook redeploy —
POST /api/stacks/redeploywith pulls and restarts all running services - OIDC / OAuth login — any OpenID Connect provider (Pocket ID, Authentik, etc.)
- Non-intrusive — calls Docker socket or plain
docker composecommands under the hood; fully compatible with manual CLI usage, no lock-in
Mount your stacks directory and the Docker socket:
# compose.yaml
services:
dockstack:
image: ghcr.io/raphaelgc/dockstack:latest # versioning is also available
ports:
- 3000:3000
volumes:
- ./stacks:/app/stacks # one subdirectory per stack
- dockstack-db:/app/data # sqlite database for auth
- /var/run/docker.sock:/var/run/docker.sock
environment:
- BETTER_AUTH_SECRET=changeme # generate with `openssl rand -hex 32`
- BETTER_AUTH_URL=https://dockstack.example.com
- ADMIN_EMAIL=you@example.com # default password is 'password'
volumes:
dockstack-db:Each stack is a subdirectory under STACKS_DIR containing a compose file:
stacks/
my-app/
compose.yaml
another-service/
compose.yaml
.env
| Variable | Required | Default | Description |
|---|---|---|---|
BETTER_AUTH_SECRET |
yes | — | Secret key for session signing (min 32 chars) |
BETTER_AUTH_URL |
yes | — | Public URL of the dockstack instance |
ADMIN_EMAIL |
yes | — | Email of the initial admin user |
OAUTH_PROVIDER_ID |
yes | — | Display name for the OAuth provider |
OAUTH_CLIENT_ID |
yes | — | OAuth client ID |
OAUTH_CLIENT_SECRET |
yes | — | OAuth client secret |
OAUTH_DISCOVERY_URL |
yes | — | OIDC discovery endpoint URL |
APP_TITLE |
no | Dockstack |
Title of the application |
SERVER_HOST |
no | localhost |
Host shown in port links in the UI |
STACKS_DIR |
no | /app/stacks |
Path to stacks directory inside container |
DATABASE_PATH |
no | /app/data/db.sqlite |
Path to SQLite auth database |
PORT |
no | 3000 |
HTTP port the server listens on |
DOCKER_CONFIG_DIR_PATH |
yes | /app/docker |
Path to Docker config directory (passed as docker --config) |
DOCKER_SYSTEM_PRUNE_CRON |
no | — | Cron expression to schedule automatic docker system prune (e.g. 0 3 * * * for 3 AM daily) |
DOCKER_SYSTEM_PRUNE_INCLUDE_VOLUMES |
no | false |
Set to true to also prune unused volumes during scheduled prune |
REDEPLOY_SKIP |
no | — | Comma-separated list of stack names to skip redeploying |
OTHER_INSTANCE_URLS |
no | — | List of other instance to have a link to, format : name1,url1;name2,url2 |
Trigger a pull + redeploy of all running stacks via API key:
curl -X POST https://dockstack.example.com/api/stacks/redeploy \
-H "x-api-key: <your-api-key>"Generate API keys from the settings page. Only running services in each stack are restarted — stopped services are left untouched.