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
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.