# site-templates Building blocks the provisioning CLI (Phase 4) renders into `deployments///`. Two parts: **base images** and **profile templates**. ## Images (`images/`) Hardened base images, built and pushed to the Forgejo registry. | Image | Base | Notes | |-------|------|-------| | `nginx` | `nginx:1.27-alpine` | Hardened `nginx.conf` + reusable snippets (`security.conf`, `fastcgi-php.conf`). Per-site server block mounted at runtime. | | `php-fpm` | `php:-fpm-alpine` | **Runs as non-root** (`www-data`). Extensions: pdo_mysql, mysqli, gd, intl, zip, opcache, exif. OPcache tuned. Built per PHP version. | Build (and Trivy-scan) locally: ```bash REGISTRY=git.example.com/heleos ./images/build.sh # build + scan REGISTRY=git.example.com/heleos PUSH=1 ./images/build.sh # also push ``` CI does the same on push — see [`.forgejo/workflows/images.yml`](../.forgejo/workflows/images.yml). WordPress uses the official `wordpress:-fpm-alpine` image directly (it bundles WordPress + php-fpm); only `custom-php` uses our `php-fpm` image. ## Profiles (`profiles/`) One directory per profile (see [../docs/04-site-profiles.md](../docs/04-site-profiles.md)). Each has a `docker-compose.yml.j2` and its nginx config template. | Profile | Containers | DB | Networks | |---------|-----------|----|---------| | `static` | nginx (web root ro) | — | proxy | | `redirect` | nginx (301/302) | — | proxy | | `custom-php` | nginx + php-fpm | optional | proxy, site, [platform] | | `wordpress` | nginx + wordpress-fpm | required | proxy, site, platform | Routing pattern (all profiles): only **nginx** carries Traefik labels and joins `proxy`; php-fpm joins the private `site` network (`_net`) and, when a DB is used, the shared internal `platform` network to reach MariaDB. nginx reaches php-fpm at `fpm:9000`. ## Render context The CLI renders the `.j2` files with these variables (source: the site's `site.yaml` + platform config): | Variable | Example | Meaning | |----------|---------|---------| | `customer` / `site` | `acme` / `shop` | Ids. | | `slug` | `acme-shop` | `-`; Docker/router key. | | `slug_underscored` | `acme_shop` | For DB names (`db_`, `u_`). | | `domains` | `[shop.acme.com]` | Host rule is `Host(\`d1\`) || Host(\`d2\`)…` | | `webroot` | `/tank/customers/acme/shop/web` | Bind-mounted web root. | | `database` | `true`/`false` | custom-php only; wordpress is always true. | | `redirect_to` / `redirect_code` | `https://acme.com` / `301` | redirect profile. | | `nginx_image` / `php_image` / `wordpress_image` | `git.example.com/heleos/nginx:latest` | Resolved image refs. | | `resources.cpu` / `resources.memory` | `1.0` / `512m` | Per-project limits. | Secrets (DB password) are **not** rendered into the compose file — they go into a git-ignored `.env` (from `secrets.enc.yaml`) that the compose reads via `env_file`. ## Provisioning note (for Phase 4) The web root dataset must be writable by the php-fpm user (`www-data`, uid 82 in these alpine images) **and** by the customer's SFTP user. Provisioning will set web-root ownership/permissions accordingly (e.g. shared group + setgid) — this is an open item to finalize in the Phase 4 CLI, not baked into these templates.