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.
| Variable | Description | Default |
|---|---|---|
DB_PATH | Path to the SQLite database file inside the container | /app/data/capitrack.db |
CAPITRACK_INIT_USERNAME | Admin username created on first run | admin |
CAPITRACK_INIT_PASSWORD | Admin password on first run. If empty, a strong random password is generated and written to the logs | (empty → random) |
CAPITRACK_BASE_CURRENCY | Base/main currency for dashboard totals on first run | EUR |
The API also understands two more variables that aren't set by default:
| Variable | Description |
|---|---|
ASPNETCORE_URLS | Listen address. The API image sets http://+:8080. |
CORS_ORIGINS | Comma-separated origins to enable a credentialed dev CORS policy. Unused in the single-origin Docker setup. |
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:
# 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:
$ 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:
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:
services:
web:
ports:
- "8088:80"
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:
# 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
.db file back into the volume.