Home Docs Configuration
Guides

Configuration

Capitrack runs with sensible defaults out of the box, so most people never touch a thing. When you do want to set the initial admin password, move the database, or change the published port, everything is driven by a handful of environment variables on the api service plus the port mapping on the web service.

Environment variables

These are set on the api service in docker-compose.yml. The CAPITRACK_INIT_* values only take effect on the first run, while the database is still empty.

VariableDescriptionDefault
DB_PATHPath to the SQLite database file inside the container/app/data/capitrack.db
CAPITRACK_INIT_USERNAMEAdmin username created on first runadmin
CAPITRACK_INIT_PASSWORDAdmin password on first run. If empty, a strong random password is generated and written to the logs(empty → random)
CAPITRACK_BASE_CURRENCYBase/main currency for dashboard totals on first runEUR

The API also understands two more variables that aren't set by default:

VariableDescription
ASPNETCORE_URLSListen address. The API image sets http://+:8080.
CORS_ORIGINSComma-separated origins to enable a credentialed dev CORS policy. Unused in the single-origin Docker setup.
Changing CAPITRACK_INIT_* after the first run has no effect — the admin user already exists. To change credentials later, use Settings → Security, or reset by clearing the data volume with docker compose down -v.

Setting the initial admin password

By default the first-run password is random and printed to the logs. To choose your own, copy the template to a .env file (it is gitignored) and edit the values before the first docker compose up:

.envini
# first-run admin account (empty DB only)
CAPITRACK_INIT_USERNAME=admin
CAPITRACK_INIT_PASSWORD=my-secure-password

# base currency for dashboard totals
CAPITRACK_BASE_CURRENCY=EUR

If you leave CAPITRACK_INIT_PASSWORD empty, read the generated password from the logs after first start:

terminalbash
$ docker compose logs api

The docker-compose stack

Capitrack is two services managed by one docker-compose.yml. The api container is internal (it only exposes 8080); the web container (nginx) is the one published to your host and reverse-proxies /api to the api container:

docker-compose.ymlyaml
services:
  api:
    build:
      context: .
      dockerfile: docker/api.Dockerfile
    restart: unless-stopped
    environment:
      - DB_PATH=/app/data/capitrack.db
      - CAPITRACK_INIT_USERNAME=${CAPITRACK_INIT_USERNAME:-admin}
      - CAPITRACK_INIT_PASSWORD=${CAPITRACK_INIT_PASSWORD:-}
      - CAPITRACK_BASE_CURRENCY=${CAPITRACK_BASE_CURRENCY:-EUR}
    volumes:
      - capitrack-dotnet-data:/app/data
      - ./transactions:/app/transactions:ro
    expose:
      - "8080"

  web:
    build:
      context: .
      dockerfile: docker/web.Dockerfile
    restart: unless-stopped
    depends_on:
      api:
        condition: service_healthy
    ports:
      - "3000:80"

volumes:
  capitrack-dotnet-data:

Changing the published port

The app is published on host port 3000 (3000:80 on the web service). To use a different port, change the left-hand side of the mapping — e.g. for 8088:

docker-compose.ymlyaml
services:
  web:
    ports:
      - "8088:80"
The repo ships a local-only docker-compose.override.yml that remaps web to 8088:80. Compose merges override files automatically, so if it's present the app comes up on that port. Edit or delete it to control the published port. The internal API port (8080) never needs to change.

Data persistence

All persistent state lives in a named Docker volume mounted at /app/data:

  • capitrack.db — the SQLite database (accounts, transactions, goals, tags, rates, the price cache and daily-wealth snapshots).
  • dp-keys/ — the DataProtection key ring. Persisting it is what keeps your auth cookie valid across container restarts (otherwise the keys rotate and everyone is logged out).
  • settings.json — written when you change the database path from Settings → Database.

The volume survives docker compose down; docker compose down -v deletes it (wiping the database).

The transactions mount

A host folder is also mounted read-only into the api container at /app/transactions as a convenient place to stage CSV files for manual import. Capitrack does not auto-import from it — you import through the UI (account → Import) or the API. See CSV import.

Backing up your data

Because all of your data is a single SQLite file in the volume, backups are entirely in your control. Copy the database file straight out of the volume:

terminalbash
# copy the live database out of the running api container
$ docker compose cp api:/app/data/capitrack.db ./capitrack-backup.db

# …or archive the whole data volume
$ docker run --rm -v capitrack-dotnet-data:/data -v $PWD:/backup \
    alpine tar czf /backup/capitrack-backup.tar.gz /data
Your data never leaves your machine. To restore, stop the stack and drop the saved .db file back into the volume.