Deployment
Run MakeV1 on one server with Docker Compose, or build the images for any host.
MakeV1 needs one service besides its own apps: Postgres. Jobs run inside the API, files can stay on the server's disk, and HTTPS comes from Caddy, so a single server runs the whole product.
One server with Docker Compose
Everything is in infra/compose: Postgres, a one-off migration step, the API, every web app, and Caddy with automatic HTTPS certificates.
- Get a server with Docker (2 vCPU and 4 GB of memory is plenty to start).
- Point DNS for your domain and its
app.,api.andadmin.subdomains at it. The docs are served at/docson the main domain. - Copy the repository to the server, then:
cd infra/compose
cp .env.example .env # set DOMAIN, ACME_EMAIL, the secrets and an email provider
docker compose up -d --buildEvery URL is derived from DOMAIN: the site at https://example.com, the dashboard at https://app.example.com, and so on. The API refuses to start with a development secret, the console email provider or the embedded database, so a misconfigured server fails at once instead of half-working.
Releases
git pull
docker compose up -d --buildThe migrate service applies new migrations before the API starts. If one fails, the API keeps running the previous release.
Releases from GitHub
Once the server works, let GitHub build and ship each release instead of building on the server:
- In the repository's Settings → Secrets and variables → Actions, add:
- Variables:
DOMAIN, plusDEPLOY_HOST,DEPLOY_USER, andDEPLOY_PATH(the folder on the server holding.env). - Secrets:
DEPLOY_SSH_KEY, a key that may log in as that user, andDEPLOY_KNOWN_HOSTS, the server's host key fromssh-keyscan <host>. Check it against the server before saving it.
- Variables:
- Tag a release:
git tag v1.0.0 && git push origin v1.0.0The release workflow:
- builds every image and publishes it to GitHub's container registry as
ghcr.io/<owner>/<app>:v1.0.0; - copies the Compose files to the server and points
.envat the new tag; - pulls the images and restarts.
The server never builds, and its registry login lasts only for that deploy. To roll back, run the workflow by hand with the previous tag and deploy only checked.
Without the DEPLOY_* variables, the workflow only publishes the images. That's useful for Kubernetes or any host that pulls images.
Scaling up, only when you need to
| Need | Change |
|---|---|
| Jobs off the API's CPU | WORKER_INLINE=false, then docker compose --profile worker up -d --scale worker=2 |
| A message broker | QUEUE_DRIVER=rabbitmq and --profile rabbitmq; see Background jobs |
| Files in object storage | STORAGE_DRIVER=s3 or azure; see Files |
| Managed Postgres | Remove the postgres service and point DATABASE_URL at it |
Backups
Postgres holds everything except uploaded files. Back it up with pg_dump on a schedule, and copy the storage volume if files stay on disk. Test a restore before you need one.
Images for any host
The root Dockerfile builds every app:
docker build --target api -t my/api .
docker build --target next --build-arg APP=app \
--build-arg NEXT_PUBLIC_API_URL=https://api.example.com -t my/app .APP is app, admin, web or docs. The Next.js apps read their NEXT_PUBLIC_* values at build time, so build them once per environment. The API image runs node dist/index.js; run node dist/db/migrate.js from the same image before each release, and node dist/worker.js for dedicated workers.
Kubernetes (Helm) and Terraform for AWS, Azure and Google Cloud are on the way.