5.5 KiB
Deploying ht_booking (Tenis Rajec)
This app ships as a single Docker container that runs behind your existing shared Caddy reverse proxy. Caddy terminates HTTPS; the app container has no host ports and is reachable only through Caddy. The SQLite database lives on a Docker volume so it survives rebuilds and restarts.
Internet ──▶ Caddy (:80/:443, HTTPS)
│ reverse_proxy ht-booking:5150 (over tenisrajec-net)
▼
ht-booking container ──▶ /usr/app/data/production.sqlite
(Docker volume: ht_booking_data)
Files in this repo that drive the deployment:
| File | Role |
|---|---|
Dockerfile |
3-stage build: CSS → Rust binary → slim runtime image |
docker-compose.prod.yml |
the app service, volume, network |
Caddyfile |
the site's reverse-proxy block (imported by central Caddy) |
config/production.yaml |
Loco production config (no secrets) |
.env.production.example |
template for secrets — copy to .env.production |
Makefile |
make up / down / logs / restart |
Prerequisites
- The server already runs the shared Caddy stack (
docker-compose.caddy.yml). - Docker +
docker-composeare installed (they already are — Caddy uses them). - You can edit DNS for
tenisrajec.sk.
One-time setup
1. Point DNS at the server
Create DNS A records so both names resolve to the server's public IP:
tenisrajec.sk A <server-ip>
www.tenisrajec.sk A <server-ip>
Do this first — Caddy needs the domain to resolve to obtain the TLS certificate. Propagation can take a while.
2. Clone the repo onto the server
cd ~
git clone <your-git-remote-url> ht_booking
cd ht_booking
(The rest of this guide assumes the repo is at ~/ht_booking.)
3. Create the shared network
The app and Caddy talk over a dedicated Docker network — same pattern as your
other projects (biomed-net, farmeris-net, …):
docker network create tenisrajec-net \
--driver bridge --opt com.docker.network.driver.mtu=1450
4. Create the secrets file
cp .env.production.example .env.production
openssl rand -hex 32 # copy the output into JWT_SECRET
nano .env.production
Fill in:
JWT_SECRET— paste theopenssloutput (required — the app won't start without it).ADMIN_EMAIL/ADMIN_PASSWORD— the single admin login, seeded on first boot.
.env.production is gitignored — it stays only on the server.
5. Hook the site into the central Caddy
Caddy must (a) import this site's Caddyfile and (b) join tenisrajec-net.
a. Add one line to the central ~/Caddyfile:
import /etc/caddy/Caddyfile_tenisrajec
b. In ~/docker-compose.caddy.yml, under the caddy service add the
Caddyfile mount to volumes: …
- ./ht_booking/Caddyfile:/etc/caddy/Caddyfile_tenisrajec
… add the network to the caddy service's networks: list …
networks:
- vonavucke-net
- biomed-net
- gitea-net
- mqtt-net
- farmeris-net
- tenisrajec-net # <-- add
… and declare it in the top-level networks: block:
tenisrajec-net:
external: true
driver: bridge
driver_opts:
com.docker.network.driver.mtu: 1450
c. Recreate Caddy so it picks up the new mount and network:
cd ~
docker-compose -f docker-compose.caddy.yml up -d
6. Build and start the app
cd ~/ht_booking
make up
The first build takes a few minutes (it compiles the Rust release binary). On first boot the app creates the SQLite database, runs all migrations, and seeds the admin account and a default court — automatically.
7. Verify
make logs # look for "listening on http://0.0.0.0:5150"
make ps # STATUS should become "healthy"
Then open https://tenisrajec.sk — Caddy will have issued the certificate.
Updating after code changes
cd ~/ht_booking
git pull
make restart
make restart rebuilds the image and recreates the container. The database
volume is untouched, so all bookings are preserved. Migrations for any new
schema run automatically on boot.
If you changed templates/CSS, the image rebuilds
app.cssitself — you do not need to runnpm run build:csson the server.
Backups
The whole database is one SQLite file inside the ht_booking_data volume.
Copy it out at any time:
docker cp ht-booking:/usr/app/data/production.sqlite ./backup-$(date +%F).sqlite
Restore by stopping the app, copying a file back, and starting it:
make down
docker cp ./backup-2026-05-16.sqlite ht-booking:/usr/app/data/production.sqlite
make up
A nightly cron job running that docker cp into a backed-up directory is
enough for this site.
Troubleshooting
| Symptom | Cause / fix |
|---|---|
App exits immediately, logs mention JWT_SECRET / config |
JWT_SECRET is empty in .env.production. Set it, make restart. |
502 Bad Gateway from Caddy |
App not up yet, or Caddy didn't join tenisrajec-net. Check make ps and step 5b. |
| Caddy can't get a certificate | DNS not pointing at the server yet, or ports 80/443 blocked. |
network tenisrajec-net not found |
Run step 3 before make up / recreating Caddy. |
| Need a shell in the container | docker exec -it ht-booking bash |
The app listens on 5150 inside its container only — it is intentionally
not published to the host. All traffic goes through Caddy.