# 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-compose` are 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 www.tenisrajec.sk A ``` 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 ```sh cd ~ git clone 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`, …): ```sh docker network create tenisrajec-net \ --driver bridge --opt com.docker.network.driver.mtu=1450 ``` ### 4. Create the secrets file ```sh cp .env.production.example .env.production openssl rand -hex 32 # copy the output into JWT_SECRET nano .env.production ``` Fill in: - `JWT_SECRET` — paste the `openssl` output (**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:` … ```yaml - ./ht_booking/Caddyfile:/etc/caddy/Caddyfile_tenisrajec ``` … add the network to the `caddy` service's `networks:` list … ```yaml networks: - vonavucke-net - biomed-net - gitea-net - mqtt-net - farmeris-net - tenisrajec-net # <-- add ``` … and declare it in the top-level `networks:` block: ```yaml 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: ```sh cd ~ docker-compose -f docker-compose.caddy.yml up -d ``` ### 6. Build and start the app ```sh 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 ```sh make logs # look for "listening on http://0.0.0.0:5150" make ps # STATUS should become "healthy" ``` Then open — Caddy will have issued the certificate. --- ## Updating after code changes ```sh 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.css` itself — you do > not need to run `npm run build:css` on the server. --- ## Backups The whole database is one SQLite file inside the `ht_booking_data` volume. Copy it out at any time: ```sh 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: ```sh 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.