Files
dvla-api/SERVER.md
Ovidiu U c4980dfdb5 Document updated per-app PHP-FPM pool and filesystem permissions
Updates SERVER.md with deploy-dvla user separation, dedicated php8.4-fpm-dvla pool configuration, explicit directory permissions for Laravel storage/cache/database, and known production gaps
2026-06-10 13:40:20 +01:00

3.9 KiB

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 (UPDATED)

  • deploy (UID 1000) — human admin, sudo, your SSH key
  • deploy-dvla (UID 1001) — owns and runs dvla-api, no sudo, your SSH key
  • www-data — nginx only, no group memberships in app users

PHP-FPM (UPDATED)

  • Pool: /etc/php/8.4/fpm/pool.d/dvla-api.conf
  • Runs as deploy-dvla
  • Socket: /run/php/php8.4-fpm-dvla.sock
  • pm.max_children = 3
  • Default www.conf disabled (renamed to www.conf.disabled)

Filesystem perms

  • /var/www/dvla-api: 2755 (traversable so nginx can reach public/)
  • App subdirs: 2750 (deploy-dvla owner+group only)
  • public/: 755/644 (world-readable, intentional)
  • .env: 600
  • storage/, bootstrap/cache/: 2770 (writable by deploy-dvla)
  • database/: 2770, database.sqlite: 660

Known gaps

  • No backups
  • No HTTPS yet
  • Token-based git auth (not SSH) — Pangolin doesn't forward SSH to Gitea o

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.