Skip to content

Docker Compose

This is the reference Compose shape for a single-host BillTracker deployment. For the full install walkthrough, start with Install with Docker.

services:
  bill-tracker:
    image: dream.scheller.ltd/null/bill-tracker:dev-v0.37.0
    container_name: bill-tracker-main
    restart: unless-stopped
    ports:
      - "3030:3000"
    environment:
      NODE_ENV: production
      TZ: America/Chicago

      # First start only. Remove or rotate after setup.
      INIT_ADMIN_USER: admin
      INIT_ADMIN_PASS: replace-with-a-long-password

      # Strongly recommended when storing bank/OIDC/SMTP/TOTP secrets.
      TOKEN_ENCRYPTION_KEY: replace-with-a-long-random-secret

      # Recommended production CSRF/cookie posture.
      CSRF_HTTP_ONLY: "true"
      CSRF_SAME_SITE: "strict"
      CSRF_SECURE: "true"

      # Optional: WebAuthn RP ID and origin for hardware-key 2FA.
      # WEBAUTHN_RP_ID: bills.example.com
      # WEBAUTHN_ORIGIN: https://bills.example.com
    volumes:
      - /portainer/hosting/bill-tracker/data:/data

Required decisions

Setting Production recommendation
image Pin the version you intend to run. Use latest only if you accept automatic drift when pulling.
container_name Use a stable name such as bill-tracker-main so logs and inspect commands are predictable.
ports Publish one host port, commonly 3030:3000, then put HTTPS in front with a reverse proxy.
volumes Mount one persistent host directory to /data. This is where the database and backups live.
TOKEN_ENCRYPTION_KEY Set a long random value and keep it outside the database.
INIT_ADMIN_* Use for first start only, then remove or rotate.

Notes

  • Keep /portainer/hosting/bill-tracker/data persistent across upgrades.
  • The database defaults to /data/db/bills.db.
  • Managed backups default to /data/backups.
  • CSRF_SECURE=true is appropriate for HTTPS. Use false only for plain HTTP development.
  • CSRF_HTTP_ONLY=true is the default. The SPA fetches the CSRF token from GET /api/auth/csrf-token and stores it in memory, so JavaScript does not need to read the cookie.
  • The image starts as root long enough to prepare /data, then runs the app as the non-root bill user.
  • The entrypoint runs migrations unless RUN_DB_MIGRATIONS=false.

Useful commands

docker compose up -d
docker compose logs -f bill-tracker
docker inspect bill-tracker-main --format '{{json .Mounts}}'
docker compose pull
docker compose up -d

See also