Appearance
Docker: compose, App Platform, AWS ECS and App Runner, Coolify
One image of the site for any host that runs containers: a VPS with docker compose, Coolify, Fly.io, AWS ECS or App Runner, DigitalOcean's App Platform. PHP-FPM and Nginx run in one container; the same image runs the worker and the scheduler with another command. What the server needs and where else to host: Deployment.
With the deploy kit
bash
php artisan kitloom:deploy-kit docker --domain=example.com| File | What it is |
|---|---|
Dockerfile | php:8.4-fpm with the extensions, Nginx and supervisord; a vendor stage that installs the packages with COMPOSER_AUTH as a build argument — the stage is not in the image, so the token never reaches its history; a stage with Node that builds the Vue and React themes when a theme needs it (PHP stays there: a TypeScript theme asks it for the page types), their SSR bundles copied into the image with Node's binary to run them; the image with the entrypoint, port 8080 and a health check on /up |
docker/ | The container's nginx.conf, php.ini (uploads up to 64 MB, OPcache without file checks, on for the console too), supervisord.conf (PHP-FPM, Nginx and, with a Vue or React theme, the SSR server php artisan inertia:start-ssr), entrypoint.sh (Laravel's caches from the container's environment, the migrations and storage:link for the site, any command as www-data for the worker and the scheduler, a managed database's CA certificate from DB_CA_CERT), auth.json.example for the token |
.dockerignore | vendor, node_modules and the built themes are made inside the build; the environment and the credentials never go in |
compose.yml | One host: the site on port 80, the worker, the scheduler, MySQL 8.4, Redis with Horizon; volumes for the uploads and the database |
deploy/docker.env | The environment of every container, and MySQL's: to fill in and keep out of the repository |
deploy/README.md | The steps for this site, and what a platform that runs the image needs |
One host with docker compose
cp docker/auth.json.example docker/auth.json, fill in your kitloom customer name and token (and the Nova license on a Nova site); adddocker/auth.jsonanddeploy/docker.envto.gitignore.deploy/docker.env:APP_KEY(php artisan key:generate --show), the database passwords, a real mailer.Build and start:
bashCOMPOSER_AUTH="$(cat docker/auth.json)" docker compose up -d --buildThe
appcontainer runs the migrations once the database answers, then serves on port 80; the worker and the scheduler start once it is healthy.docker compose logs -fwatches it come up.The first administrator — or WordPress's database loaded into the
dbcontainer instead, withDB_PREFIXindeploy/docker.env:bashdocker compose exec app php artisan kitloom:user --login=admin [email protected] --no-interactionHTTPS: a reverse proxy in front of port 80 — Caddy or Traefik in a container of their own, or the host's Nginx — with
APP_URLas the address it answers on.
A new release is the same docker compose up -d --build after a pull; compose replaces the containers one by one. The uploads stay in the uploads volume, the database in db.
A platform that runs the image
ECS, App Runner, App Platform, Coolify, Fly.io: the image is built from the Dockerfile with COMPOSER_AUTH as a build-time variable, and the platform gives the containers their environment — deploy/docker.env without the MYSQL_* lines, the database's address from the platform. Three things change without a shared disk:
- The uploads go to a bucket:
MEDIA_DISK=s3with theAWS_*variables of S3 or Spaces (composer require league/flysystem-aws-s3-v3when it is not installed yet), andHEALTH_EPHEMERAL_DISK=true, so Site Health checks that nothing is left on a container's disk. - The worker and the scheduler are services of the same image with
php artisan queue:work --sleep=3 --tries=3 --max-time=3600(orphp artisan horizon) andphp artisan schedule:workas their command: the entrypoint runs any command as the web user, with the caches built first. - Import & Export reads the WXR file from
IMPORT_EXPORT_DISK, and the worker that imports it runs in another container: a private bucket for it, as aprivatedisk added toconfig/filesystems.php, andIMPORT_EXPORT_DISK=private.
KITLOOM_MODULES_CACHES=deploy stays: a module switched on the Modules screen asks for new containers, which build their caches with every module on (Enabling & Disabling).
DigitalOcean App Platform
bash
php artisan kitloom:deploy-kit digitalocean-app --domain=example.comThe same image, and .do/app.yaml: the web service, the worker and the scheduler as components of the image, built from the GitHub repository on every push; a managed MySQL cluster attached by name and bound into the environment (${db.HOSTNAME} and the rest, the CA certificate through the entrypoint); the uploads in a Space behind its CDN; Valkey with Horizon; the domain. The secrets — APP_KEY, COMPOSER_AUTH for the build, the Space's key and secret — are filled in the dashboard, so the spec can be committed.
In order: a Space for the uploads with its CDN and an access key; the database cluster, made before the app, since App Platform's own development databases are Postgres only (doctl databases create example-com-db --engine mysql --region fra1 --size db-s-1vcpu-1gb); the repository connected to DigitalOcean; doctl apps create --spec .do/app.yaml; the secrets in Settings → App-Level Environment Variables, which starts the build; the first administrator from the web component's Console; the domain's DNS as Settings → Domains says. A change to the spec goes up with doctl apps update <app-id> --spec .do/app.yaml; a change to the code, with a push. deploy/README.md has every step for this site.
AWS ECS and App Runner
The image as above, pushed to ECR (aws ecr get-login-password | docker login …, docker build --build-arg COMPOSER_AUTH="$(cat docker/auth.json)" -t <account>.dkr.ecr.<region>.amazonaws.com/example-com ., docker push). App Runner runs it as one service from the image with the environment in its console; ECS as a task definition with three containers of the image — the site on port 8080, the worker and the scheduler with their command — behind a load balancer whose health check is /up. RDS for MySQL (its endpoint as DB_HOST; the RDS CA bundle as MYSQL_ATTR_SSL_CA if the parameter group requires TLS), S3 for the uploads, SES for the mail. Both builders pass COMPOSER_AUTH as a build argument; nothing of it is in the pushed image.