Docker Compose on a VPS
CrashCart, Postgres and Caddy (for automatic HTTPS) on one Linux server. Every step below has been run as written on a fresh Ubuntu machine.
You need
- A VPS with a public IP and ports 80 and 443 open (1 GB RAM is enough to start)
- A domain, e.g.
crashcart.example.com, with an A record pointing at that IP - SSH access as a user who can run
sudo
1. Install Docker
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
newgrp docker
docker compose version2. Get the compose file
git clone https://github.com/crashcartapp/crashcart
cd crashcart3. Configure CrashCart
Open docker-compose.yml and set these in the crashcart service's environment:
PUBLIC_URL: https://crashcart.example.com
API_KEYS: "<long random string>"
VIEWER_PASSWORD: "<another long random string>"Generate the strings with openssl rand -hex 32.
Also change the Postgres password from crashcart in both places it appears (POSTGRES_PASSWORD on db, DATABASE_URL on crashcart).
4. Add Caddy
Caddy gets a certificate from Let's Encrypt and renews it by itself. Append this service to docker-compose.yml and a caddy volume:
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy:/data
depends_on:
- crashcart
volumes:
pgdata:
caddy:Create Caddyfile next to it, with your domain:
crashcart.example.com {
reverse_proxy crashcart:8080
request_body {
max_size 25MB
}
}Remove the ports: block from the crashcart service — only Caddy needs to be reachable from outside.
5. Start
docker compose up -d
docker compose logs -f caddy # wait for "certificate obtained", then Ctrl-CCheck from your laptop:
curl https://crashcart.example.com/health
# {"status":"ok"}6. Create a project
docker compose exec crashcart /crashcart project shop-ios "Shop app (iOS)" ios
# project shop-ios (id 1)
# DSN: https://<key>@crashcart.example.com/1Open https://crashcart.example.com (any username, your VIEWER_PASSWORD), then paste the DSN into the SDK — Connect an SDK.
Go through Before going live once.
Upgrading
cd crashcart
docker compose pull
docker compose up -dlatest follows the newest release. To pin a version, set image: ghcr.io/crashcartapp/crashcart:0.1.0 (or :0.1 for the newest patch release of that line) in docker-compose.yml.
iOS crashes
To symbolicate iOS / macOS crashes, uncomment the symbolicate service in docker-compose.yml and add SYMBOLICATE_URL: http://symbolicate:8080 to crashcart's environment. Android and JavaScript need nothing extra.
Backups
docker compose exec crashcart /crashcart export > backup-$(date +%F).ndjsonPut that in cron. Restore with crashcart import < backup.ndjson. See Operations.
If something doesn't work
| Symptom | Check |
|---|---|
certificate obtained never appears | The A record must point at this server and ports 80/443 must be open (sudo ufw status, cloud firewall). Caddy retries by itself once DNS is right |
/health returns 503 | Postgres isn't up yet: docker compose logs db |
The DSN says http://localhost:8080 | PUBLIC_URL isn't set; fix it and docker compose up -d |
SDK gets 401 | Wrong key in the DSN. docker compose exec crashcart /crashcart rotate-key shop-ios prints a fresh one |
Large crash reports fail with 413 | Raise max_size in the Caddyfile |