profileShare

rasmusjy / splitapp-backend-clean-onion

Read-only snapshot

No repository description.

main default branch 429 files Expires Sep 13, 2026, 9:06 AM
DEPLOY.md 4,478 bytes

Deploy — travel.rasmusj.com

Production deploy on the Hetzner VPS. Caddy (already running in /opt/caddy/) terminates HTTPS and reverse-proxies to this app's container over the shared web Docker network.

Internet → Caddy (:80/:443, HTTPS) → [web network] → csweb-travel (:8080)
                                                          ↓ [internal network]
                                                      db (Postgres, no public port)

Files in this repo

File Purpose
Dockerfile .NET 10 SDK build → aspnet runtime. Source stays under SplitApp/, so COPY paths are unchanged.
docker-compose.prod.yml Production stack. No public ports; secrets from .env.
.env.example Template. Copy to .env and fill with strong values.
.env Real secrets — gitignored, never commit. Create on the server.

Stage 1 — manual first deploy

1. Deploy key (read-only access to the private repo)

ssh <user>@<server-ip>
ssh-keygen -t ed25519 -C "csweb-travel-deploy" -f ~/.ssh/csweb-travel -N ""
cat ~/.ssh/csweb-travel.pub

Add the printed public key to the GitHub repo → Settings → Deploy keys → Add deploy key (read-only is enough). Then make git use it:

cat >> ~/.ssh/config <<'EOF'

Host github-csweb-travel
    HostName github.com
    User git
    IdentityFile ~/.ssh/csweb-travel
    IdentitiesOnly yes
EOF

2. Clone

cd /opt/projects
git clone git@github-csweb-travel:<your-user>/<repo>.git csweb-travel
cd csweb-travel

3. Secrets

Create .env in the repo root. Use the values prepared locally, or regenerate:

cat > .env <<EOF
POSTGRES_PASSWORD=$(openssl rand -base64 24 | tr -d '/+=' | cut -c1-32)
JWT_KEY=$(openssl rand -base64 48 | tr -d '\n')
SEED_ADMIN_PASSWORD=Adm!n.$(openssl rand -base64 9 | tr -d '/+=')9
EOF
chmod 600 .env
cat .env   # note the SEED_ADMIN_PASSWORD — that's the admin@taltech.ee login

4. Build & start

docker compose -f docker-compose.prod.yml up -d --build
docker compose -f docker-compose.prod.yml logs -f app   # watch migrate + seed

⚠️ The .NET build is the heaviest step. On a 4 GB server it should fit, but if the build gets OOM-killed, add temporary swap: sudo fallocate -l 2G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile

Confirm the container is on the web network and healthy:

docker network inspect web --format '{{range .Containers}}{{.Name}} {{end}}'
# should list: caddy ... csweb-travel

5. Caddy

Append to /opt/caddy/Caddyfile:

travel.rasmusj.com {
    reverse_proxy csweb-travel:8080
}

Note: target is csweb-travel (the container_name), not web. A unique name avoids DNS collisions once other apps join the shared web network.

Reload (no downtime for other sites):

cd /opt/caddy
docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile
# or, if the Caddyfile is bind-mounted and that fails:
docker compose restart caddy

6. Test

curl -I https://travel.rasmusj.com    # expect 200/302/308, valid Let's Encrypt cert

Open https://travel.rasmusj.com and log in: admin@taltech.ee / <SEED_ADMIN_PASSWORD from .env>.

Updating later

cd /opt/projects/csweb-travel
git pull
docker compose -f docker-compose.prod.yml up -d --build

Migrations run automatically on boot (MigrateDatabase=true). Seeding is idempotent (guards on existing rows), so it won't duplicate data on restart.

Security notes

  • DB password & JWT key live only in .env (gitignored) and are injected as env vars. JWT__Key overrides the placeholder in appsettings.json.
  • Seed admin password is read from SEED_ADMIN_PASSWORD on first boot (AppDataInit.SeedIdentity). This replaces the well-known course default Kala.12345, so the public admin login is strong from the very first deploy.
  • The other seeded demo accounts (user@, alice@, …) still use Kala.12345. They are non-admin demo users. Remove them from InitialData.cs if you don't want demo logins.
  • Postgres has no published port — it's reachable only by the app over internal.
  • To rotate any secret: edit .env, then docker compose -f docker-compose.prod.yml up -d. (Changing SEED_ADMIN_PASSWORD after first boot has no effect — the user already exists; change that password through the app instead.)