InkApex docs
Reference

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.

  1. Get a server with Docker (2 vCPU and 4 GB of memory is plenty to start).
  2. Point DNS for your domain and its app., api. and admin. subdomains at it. The docs are served at /docs on the main domain.
  3. 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 --build

Every 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 --build

The 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:

  1. In the repository's Settings → Secrets and variables → Actions, add:
    • Variables: DOMAIN, plus DEPLOY_HOST, DEPLOY_USER, and DEPLOY_PATH (the folder on the server holding .env).
    • Secrets: DEPLOY_SSH_KEY, a key that may log in as that user, and DEPLOY_KNOWN_HOSTS, the server's host key from ssh-keyscan <host>. Check it against the server before saving it.
  2. Tag a release:
git tag v1.0.0 && git push origin v1.0.0

The 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 .env at 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

NeedChange
Jobs off the API's CPUWORKER_INLINE=false, then docker compose --profile worker up -d --scale worker=2
A message brokerQUEUE_DRIVER=rabbitmq and --profile rabbitmq; see Background jobs
Files in object storageSTORAGE_DRIVER=s3 or azure; see Files
Managed PostgresRemove 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.

On this page