Contents

On a server or a platform

Any platform that runs a Dockerfile or a container image works. The steps are the same everywhere; only the names of the settings differ.

1. Build the image #

Either let the platform build from the repository's Dockerfile, or build in CI and push the image to a registry the platform pulls from. Building needs about 4 GB of memory, so a small server is better off pulling a ready image.

2. Get a database #

A managed MongoDB or PostgreSQL service, or a database container on the same server. Keep it private to the site: only the container needs to reach it. See The database.

3. Set the environment #

At least:

DATABASE_URL=mongodb://…
PAYLOAD_SECRET=…            # openssl rand -hex 32, kept the same from now on
NEXT_PUBLIC_SERVER_URL=https://www.example.com

Then the optional services you use: SMTP for email, ANTHROPIC_API_KEY for AI translation, and so on (Environment variables). Use the platform's secret settings for secrets.

4. Mount the uploads volume #

Give the container a persistent volume at /app/uploads. Without one, every uploaded image and file is lost when the container is replaced. The image creates one folder per upload collection inside it.

A platform without persistent disks needs the uploads in a bucket instead. The kit puts them on Vercel Blob when BLOB_READ_WRITE_TOKEN is set (see On Vercel); another storage plugin, such as Payload's S3 adapter, can take its place in src/plugins/storage.ts. Every upload collection must be listed in it, the media library and translation-files alike.

5. Route traffic and HTTPS #

The container listens on port 3000 (or PORT). Point the platform's domain or your reverse proxy at it and let that handle HTTPS. The site address in NEXT_PUBLIC_SERVER_URL must match the address visitors use.

6. Health check #

Use GET /api/health: 200 while the server and the database are up, 503 when the database does not answer. The image has a HEALTHCHECK with it built in.

7. Background jobs #

Nothing to set up for one container: the server runs scheduled publishing and the other background work itself, once a minute. Running more than one container, or a platform that stops idle containers? Read Background jobs.

8. First start #

Open /admin on the new site. On an empty database it asks for the first user, who becomes the admin.

Search engines are welcome by default. On a staging copy, set Site settings → Behaviour → Search engines to "Keep out", which changes robots.txt; the demo seed does that itself.

Updating #

Deploy a new image with the same environment and the same volume. Logins survive as long as PAYLOAD_SECRET stays the same. On PostgreSQL, the image applies new migrations when it starts.

Back up both the database and the uploads volume: the database names the files, the volume holds them.

Platform notes #

  • Coolify: a "Dockerfile" resource (or a "Docker image" one, from your registry). Add the variables under Environment Variables, a persistent storage mounted at /app/uploads, and /api/health as the health check path.
  • Railway: a service from the repository or from an image. Add the variables, and a volume mounted at /app/uploads.
  • A VPS with Docker Compose: take the app service from docker-compose.yml into the server's compose file, with a managed database or a database service beside it, and a reverse proxy (Caddy, Traefik, nginx) in front for HTTPS.
  • Without Docker: pnpm install, pnpm build and pnpm start with Node 22, under a process manager. Uploads go to uploads/ in the working directory, so keep that folder across releases.

These guides ship with Tolk Starter 0.6.0: buyers get the same pages in their repository, next to the code they describe.