Deployment

Run PhlatPage with Docker Compose, keep per-deployment config in env.json, and ship releases with Deployer.

Layout

A deployment has two halves. phlat/, site/ and vendor/ are code, rebuilt from git on every release. data/ is state: pages/, users/, git/ (the History deploy key), storage/ (the registry, locks and rate limits), cache/ (compiled views and the like, which can be deleted at any time) and env.json. State is never baked into the image and survives redeploys, except cache/, which each release makes for itself.

Docker Compose

compose.yaml runs the app image with the project mounted at /app:

services:
  app:
    image: git.cohere.digital/phlat/frank:latest
    restart: unless-stopped
    user: 1000:1000
    volumes:
      - .:/app
      - ./phlat/Caddyfile:/etc/frankenphp/Caddyfile
    ports:
      - 10000:80

The image is FrankenPHP with PHP 8.4, gd, imagick and git. Put a reverse proxy in front for TLS; Caddy trusts private-range proxies for forwarded headers.

compose.yaml also defines a PHP-FPM and Caddy pair on port 19000 (phlat/Caddyfile.fpm) as an alternative.

Config

Config merges in order, later wins: phlat/config.json, site/config.json, data/env.json. Put secrets and per-host overrides in data/env.json, which is gitignored. data/env.cli.json merges on top for CLI runs only.

Deployer

deploy.php ships the code to a host over SSH. Start from phlat/examples/deploy.example.php and set the repository, host and deploy_path. Host credentials go in the gitignored deploy.env.php.

vendor/bin/dep deploy domain.com

Each release is cloned from git and installed with composer install --no-dev. These paths are shared between releases and symlinked in:

  • data/env.json and data/env.cli.json
  • data/pages, data/users, data/git, data/storage
  • site/public

data/cache is not shared. Each release starts with its own, so a release never serves what an earlier one rendered.

The app container restarts after a deploy or rollback, so PHP's opcache and realpath cache pick up the new release at once.

Build CSS and JS locally first. The deploy does not run a build step.

Reusable image

phlat/Dockerfile.dist builds a runtime image with phlat/ and vendor/ baked in and no site/. A downstream project mounts its own site/ and data/ over it:

services:
  app:
    image: git.cohere.digital/phlat/page:vX.Y.Z
    volumes:
      - ./site:/app/site
      - ./data:/app/data

Backups

data/ is the state to protect. One command backs up pages (collections included), users, git/ and the env files; restore with backup:restore. storage/ and cache/ are left out: restore rebuilds the registry from the pages and empties the cache.

docker compose exec app php phlat/addons/cli/phlat backup:create