DockLogDockLogBlog
5 min readDockLog

A compose file you can leave running

DockLog on Docker Compose, smoke test, auth, proxy settings, backups. The file we actually deploy.

Most DockLog installs I've seen are a single compose file on a VPS or a NUC in a closet. The image is aimldev/docklog:latest (amd64 and arm64), API, Vue UI, SQLite for users, all in one container. No agent, no second service.

Need Docker and Compose first? See Install Docker Engine and Install Docker Compose (steps from official Docker documentation).

This post walks from "does it run?" to something you'd leave up for months.

Smoke test (private network only)

yaml
services:
  docklog:
    image: aimldev/docklog:latest
    container_name: docklog
    ports:
      - "8888:8000"
    environment:
      - DISABLE_AUTH=true
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    restart: unless-stopped

http://your-host:8888, you should see containers within a few seconds.

Don't expose DISABLE_AUTH past a LAN you control. Anyone with the URL can touch the Docker socket through the UI.

Auth + persistence

bash
mkdir -p ~/docklog/data
cd ~/docklog
yaml
services:
  docklog:
    image: aimldev/docklog:latest
    container_name: docklog
    ports:
      - "8888:8000"
    environment:
      SECRET_KEY: ${SECRET_KEY}
      DB_PATH: /app/data/docklog.db
      CLIENT_ACCESS: strict
      ENV: production
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./data:/app/data
    restart: unless-stopped

.env:

text
SECRET_KEY=<output of openssl rand -hex 32>

Generate the key:

bash
openssl rand -hex 32

Log in, change the default password immediately. Credentials are in the getting started guide.

DB_PATH is what makes users, RBAC, alert config, and audit survive restarts. Without it, you're back to defaults on every docker compose up.

Behind nginx or Caddy

Bind to loopback and tell DockLog about the proxy:

yaml
ports:
      - "127.0.0.1:8888:8000"
    environment:
      TRUST_PROXY: "true"
      ALLOWED_ORIGINS: https://docklog.example.com

Full nginx block in the reverse proxy post.

Never skip ALLOWED_ORIGINS when using a public hostname. Login redirect loops are almost always origin mismatch plus missing TRUST_PROXY.

Restart buttons (optional)

Server gate first, user permission second:

yaml
ALLOW_RESTART: "true"

Then tick can_restart per user in Admin. No ALLOW_RESTART on the server = no restart for anyone, admin or not.

Same pattern for stop, start, delete, shell. See RBAC post for the full two-layer model.

Docker and K8s on one host

Homelab classic: compose apps plus k3s:

yaml
environment:
      RUNTIME_MODE: both
      KUBECONFIG: /app/kube/config
      K8S_NAMESPACES: default,staging,production
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./data:/app/data
      - ~/.kube/config:/app/kube/config:ro

RUNTIME_MODE=docker is default if you only have the socket.

UI gets a Docker/K8s toggle on the logs page. Permissions use the same allowed_containers field for both. More in K8s without Loki.

Production-shaped compose (copy-paste baseline)

Everything from above in one file:

yaml
services:
  docklog:
    image: aimldev/docklog:latest
    container_name: docklog
    ports:
      - "127.0.0.1:8888:8000"
    environment:
      SECRET_KEY: ${SECRET_KEY}
      DB_PATH: /app/data/docklog.db
      CLIENT_ACCESS: strict
      ENV: production
      TRUST_PROXY: "true"
      ALLOWED_ORIGINS: https://docklog.example.com
      ALLOW_RESTART: "true"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./data:/app/data
    restart: unless-stopped
    # Optional: cap memory on tiny VPS
    mem_limit: 256m

Swap ALLOWED_ORIGINS for your real URL. Add kubeconfig mount and RUNTIME_MODE if needed.

Environment reference (common vars)

VariablePurpose
SECRET_KEYSession signing; required with auth
DB_PATHSQLite path for users, RBAC, alerts
CLIENT_ACCESSstrict for production browser clients
ENVproduction disables debug behaviors
TRUST_PROXYtrue behind nginx/Caddy
ALLOWED_ORIGINSComma-separated browser origins
ALLOW_RESTART etc.Server-side action gates
RUNTIME_MODEdocker, kubernetes, or both
K8S_NAMESPACESNamespace ceiling for K8s mode
DISABLE_AUTHSmoke test only; never on the internet

Full list: getting started guide.

Files on disk

text
~/docklog/
├── docker-compose.yaml
├── .env
├── data/docklog.db
└── backups/          # optional

Add .env to .gitignore. SECRET_KEY in git is a bad week.

Back up docklog.db: users, RBAC, alert config. Quick cold copy:

bash
docker compose stop docklog
cp data/docklog.db backups/docklog-$(date +%F).db
docker compose start docklog

For zero-downtime-ish backup on a quiet host, SQLite copy while running usually works, but stop/start is safer before upgrades.

Upgrades

bash
docker compose pull && docker compose up -d

Data survives in ./data. Glance at releases for anything breaking.

After upgrade: log in, spot-check container list, tail one log stream, hit Test on a notification channel if you use alerts.

Stuff that breaks

SymptomLikely cause
Empty container listSocket not mounted; check ls -la /var/run/docker.sock
Login redirect loopALLOWED_ORIGINS doesn't match browser URL
Can't write databaseOwnership on ./data vs container UID
K8s tab emptyWrong KUBECONFIG path or namespace not in K8S_NAMESPACES
Logs freeze after 60sReverse proxy timeout; not compose
403 on APICLIENT_ACCESS=strict without proper client

Logs freezing behind a proxy: see reverse proxy post for proxy_read_timeout.

What to do after compose works

  1. RBAC before adding users.
  2. Reverse proxy + TLS before sharing a URL.
  3. Alerts when you want Slack pings, not staging noise.
  4. Native app if you tail from a phone regularly.

Why bother with self-hosted at all: why self-hosted post.

Continue reading