Skip to content

Docker

Punchlist ships as one image that serves both the web app and the API.

registry.gitlab.com/killswitch10/punchlist

Image tags

Tag What it is
latest The newest release.
0.2 The newest 0.2.x release: fixes, never a breaking change. Recommended for automatic updates.
0.2.1 Exactly that release.
edge The current main branch, built on every merge. For testing; not for data you care about.
sha-abc1234 One main commit.

Every image runs on linux/amd64 and linux/arm64 (a Raspberry Pi 4 or 5 with a 64-bit OS works).

Run it

docker run -d --name punchlist --restart unless-stopped \
  -p 8080:8080 \
  -v punchlist-data:/data \
  -v punchlist-files:/files \
  -e TZ=Europe/Berlin \
  registry.gitlab.com/killswitch10/punchlist:latest

Then read the first admin password with docker logs punchlist and open http://localhost:8080. For anything longer-lived, use Docker Compose.

To keep settings in a file, pass an env file:

docker run -d --name punchlist -p 8080:8080 -v punchlist-data:/data -v punchlist-files:/files \
  --env-file punchlist.env registry.gitlab.com/killswitch10/punchlist:latest
punchlist.env
# An env file for `docker run --env-file punchlist.env` or compose `env_file: punchlist.env`.
# Every line is NAME=value; no quotes, no spaces around `=`. Delete what you do not need: every
# setting has a default. The full list: docs/configuration/reference.md

TZ=Etc/UTC
PUBLIC_URL=https://tasks.example.com

# Accounts
ACCOUNT_DELETE_GRACE_DAYS=7
# ADMIN_PASSWORD_FILE=/run/secrets/admin_password

# Backups (snapshots in /data/snapshots)
BACKUP_SCHEDULE=daily
SNAPSHOT_KEEP=14

# Logs
LOG_LEVEL=information
LOG_FORMAT=text
# LOG_LOKI_URL=http://loki:3100

# Behind a reverse proxy that is not on a private network:
# TRUSTED_PROXIES=203.0.113.7

What is inside

  • A chiseled .NET base: no shell, no package manager, nothing to log into. Runs as uid 1654 (the image's app user), never root.
  • Listens on port 8080 (ASPNETCORE_HTTP_PORTS).
  • Keeps its data in /data: the SQLite database, snapshots (/data/snapshots), log files (/data/logs) and audit archives (/data/audit). Mount a volume there.
  • Keeps attached files in /files (ATTACHMENT_DIR), apart from the database, since that is the folder that grows. Mount a volume there too, on bigger or slower storage if you like; without one, Docker makes an anonymous volume that is easy to lose when the container is recreated.
  • A health check that runs punchlist healthcheck (there is no curl inside); docker ps shows healthy once the server answers.
  • The punchlist command for maintenance, run with docker exec: see the command line reference.

Data folder permissions

A named volume (-v punchlist-data:/data) works as is. A bind mount of a host folder must be writable by uid 1654:

mkdir -p ./data ./files && sudo chown 1654:1654 ./data ./files

or run the container as your own user (who then owns the folder): --user 1000:1000.

Secrets

Every setting also reads from a file: add _FILE to its name. That is how Docker and Compose secrets work:

docker run ... -e ADMIN_PASSWORD_FILE=/run/secrets/admin_password \
  -v ./admin_password:/run/secrets/admin_password:ro ...

One trailing newline in the file is ignored. Setting both NAME and NAME_FILE is an error. Values can also be encrypted.

Building the image yourself

git clone https://gitlab.com/killswitch10/punchlist.git && cd punchlist
docker build -t punchlist:local .
# both architectures:
docker buildx build --platform linux/amd64,linux/arm64 -t punchlist:local .