Documents Hetzner CX23 hosting setup, nginx/PHP-FPM stack, SQLite database configuration, and deployment architecture for single-app deployment
93 lines
3.5 KiB
Markdown
93 lines
3.5 KiB
Markdown
# Server — dvla-api
|
|
|
|
Production host for the `dvla-api` Laravel application. Single VPS, single app.
|
|
This file is the source of truth for how the box is configured. Update it when
|
|
you change the box.
|
|
|
|
## Host
|
|
|
|
- **Provider / type:** Hetzner Cloud CX23 (2 vCPU, 4 GB RAM, 80 GB disk)
|
|
- **OS:** Ubuntu 24.04 LTS (noble)
|
|
- **Public IP:** 37.27.203.46
|
|
- **Hostname:** ubuntu-4gb-dvla-api
|
|
- **Swap:** 2 GB swapfile at `/swapfile` (mounted via `/etc/fstab`)
|
|
|
|
## Users
|
|
|
|
- `root` — break-glass admin only
|
|
- `deploy` (UID 1000) — owns the app directory, performs deployments
|
|
- `www-data` — nginx + PHP-FPM runtime user; group-shared with `deploy` for file access
|
|
|
|
> **Note:** This box runs **one** app. If a second app is ever added, the
|
|
> default PHP-FPM `www` pool MUST be split into per-app pools running as
|
|
> per-app system users, or the apps will be able to read each other's `.env`
|
|
> and SQLite files. See the earlier project chats for the multi-app layout.
|
|
|
|
## Web stack
|
|
|
|
- **nginx** 1.24 — site config: `/etc/nginx/sites-available/dvla-api`
|
|
- Listens on `:80` (HTTP only — TLS pending)
|
|
- `server_name 37.27.203.46;`
|
|
- Document root: `/var/www/dvla-api/public`
|
|
- **PHP-FPM** 8.4 — pool: default `www.conf`
|
|
- Runs as `www-data:www-data`
|
|
- Socket: `/run/php/php8.4-fpm.sock`
|
|
- `pm = dynamic`, `pm.max_children = 5` (CX23-appropriate)
|
|
|
|
## Application
|
|
|
|
- **Path:** `/var/www/dvla-api`
|
|
- **Owner:** `deploy:www-data`, directories have SGID bit (`drwxrwsr-x`) so
|
|
new files inherit the `www-data` group automatically
|
|
- **Framework:** Laravel 11 on PHP 8.4
|
|
- **Git remote:** SSH to Gitea via deploy key (see `DEPLOY.md`)
|
|
- **Branch deployed:** `main`
|
|
|
|
## Database — SQLite
|
|
|
|
- **File:** `/var/www/dvla-api/database/database.sqlite`
|
|
- **Perms:** file `660`, directory `770`, both owned `deploy:www-data`
|
|
- Directory must be writable by `www-data` so SQLite can create
|
|
`-journal` / `-wal` / `-shm` siblings
|
|
- **Journal mode:** WAL (enabled with
|
|
`sqlite3 database/database.sqlite "PRAGMA journal_mode=WAL;"`)
|
|
- **Backups:** _<TODO: document backup mechanism — see Backups section>_
|
|
|
|
`SESSION_DRIVER`, `CACHE_STORE`, and `QUEUE_CONNECTION` are all `database`,
|
|
meaning sessions/cache/queue tables live in the same SQLite file as app
|
|
data. Acceptable for low traffic; revisit if write contention shows up
|
|
(`database is locked` errors in `storage/logs/laravel.log`).
|
|
|
|
## Queue / scheduler
|
|
|
|
- **Worker:** _<TODO: either set `QUEUE_CONNECTION=sync` in `.env` or
|
|
install the systemd unit below>_
|
|
- **Scheduler:** _<TODO: cron entry for `php artisan schedule:run` if any
|
|
scheduled tasks exist>_
|
|
|
|
## Network / firewall
|
|
|
|
- **UFW:** _<TODO: confirm enabled; document allowed ports>_
|
|
- **Open ports (verified via `ss -tlnp`):**
|
|
- 22 — SSH
|
|
- 80 — HTTP (nginx)
|
|
- 53 — systemd-resolved, loopback only (not public)
|
|
- **TLS:** _<TODO: not yet configured. Plan: domain → Let's Encrypt via certbot>_
|
|
|
|
## Backups
|
|
|
|
_<TODO. SQLite makes this easy: a single file. Suggested approach:
|
|
nightly cron on `deploy` running `sqlite3 database/database.sqlite ".backup
|
|
/var/backups/dvla-api/db-$(date +\%F).sqlite"` followed by offsite copy
|
|
(rsync to home server / S3 / Hetzner Storage Box).>_
|
|
|
|
## Known divergences from the multi-app pattern in project chats
|
|
|
|
This box was set up for a single app and intentionally simplified:
|
|
- Single `deploy` user instead of `deploy-<appname>`
|
|
- Default `www-data` PHP-FPM pool instead of a per-app pool
|
|
- No Redis (cache/sessions/queue all on SQLite)
|
|
|
|
These are acceptable as long as this box hosts one app. Revisit the older
|
|
project chats before adding a second app to this VPS.
|