Strapi

Strapi is an open source headless CMS built with Node.js

Updated

Strapi is a headless CMS you deploy as your own project rather than from the upstream monorepo. A project is created with npx create-strapi@latest, lives in its own Git repository, and ships an admin panel that is built once per deploy. This guide covers deploying that project repository to Hatchbox with PostgreSQL.

Requirements

Create a PostgreSQL database from the Databases tab and keep the variable named DATABASE_URL; the generated config/database file reads it as the connection string.

Strapi supports the active Node LTS releases (22, 24 and 26). Add a .nvmrc with the version you develop on, for example 22, so Hatchbox installs the same one. Building the admin panel uses about 2 GB of RAM, so use a server with at least 4 GB.

Repository

Create the project locally and push it to Git:

npx create-strapi@latest my-project --dbclient postgres --dbhost localhost --dbport 5432 --dbname strapi --dbusername strapi --dbpassword strapi --skip-cloud --no-run

--dbclient must be given together with all the --db* connection flags. Their values only go into your local .env; on Hatchbox DATABASE_URL takes precedence. Choosing postgres adds the pg driver to package.json. If your project already exists with SQLite, run npm install pg and commit the change. Commit package-lock.json as well; Hatchbox runs npm install when it finds one.

The generated .env is ignored by Git, so its values go in the Environment tab instead. Add the two .hatchbox scripts below, then set your repository URL and branch on the app.

Environment Variables

APP_KEYS=<key1>,<key2>,<key3>,<key4>
API_TOKEN_SALT=<secret>
ADMIN_JWT_SECRET=<secret>
TRANSFER_TOKEN_SALT=<secret>
JWT_SECRET=<secret>
ENCRYPTION_KEY=<secret>
DATABASE_CLIENT=postgres
DATABASE_SSL=false

Copy the secrets from the .env that create-strapi generated, or create new ones with openssl rand -base64 16. DATABASE_URL comes from the attached database. Strapi listens on HOST=0.0.0.0 and PORT by default, and Hatchbox provides PORT.

Do not set NODE_ENV=production in the Environment tab: npm would then skip the dev dependencies (TypeScript, type definitions) that the build needs. The scripts below set it per command instead.

Build Scripts

.hatchbox/pre-build

The local upload provider writes to public/uploads, which must survive deploys:

#!/usr/bin/env bash
set -e

mkdir -p $DIR/shared/uploads
rm -rf public/uploads
ln -s $DIR/shared/uploads public/uploads

Skip this if you use an upload provider plugin such as S3.

.hatchbox/post-build

Hatchbox only installs dependencies, so build the admin panel (and compile TypeScript) here. Node's default heap limit is a quarter of the server's RAM, which is too small for the admin build on servers under 8 GB, so the script raises it:

#!/usr/bin/env bash
set -e

NODE_OPTIONS=--max-old-space-size=2048 NODE_ENV=production npm run build

Processes

  1. web, web servers: NODE_ENV=production npm run start

Strapi runs its database migrations when it starts, so no separate migration step is needed.

First Login

Open /admin on your domain. The registration form creates the first administrator account.

Notes

Uploads live in shared/uploads, so keep the app on a single server unless you switch to a cloud upload provider.

Content types are defined in code under src/api, so create them locally, commit, and deploy; entries are added through the admin panel on the server. Upgrade by running npx @strapi/upgrade latest locally, committing, and deploying.