Strapi is the leading open-source headless CMS built with Node.js. Version 5 brings improved performance, better TypeScript support, and a refined content API. This guide covers deploying Strapi v5 on a VPS with PostgreSQL for production use.
Project Setup
# Create new Strapi project with TypeScript
npx create-strapi-app@latest my-cms --typescript
cd my-cms
# Or with specific database
npx create-strapi-app@latest my-cms --dbclient=postgres --typescript
Database Configuration
// config/database.ts
export default ({ env }) => ({
connection: {
client: "postgres",
connection: {
host: env("DATABASE_HOST", "127.0.0.1"),
port: env.int("DATABASE_PORT", 5432),
database: env("DATABASE_NAME", "strapi"),
user: env("DATABASE_USERNAME", "strapi"),
password: env("DATABASE_PASSWORD", ""),
ssl: env.bool("DATABASE_SSL", false) && {
rejectUnauthorized: false,
},
},
pool: {
min: env.int("DATABASE_POOL_MIN", 2),
max: env.int("DATABASE_POOL_MAX", 10),
},
},
});
Production Server Configuration
// config/server.ts
export default ({ env }) => ({
host: env("HOST", "0.0.0.0"),
port: env.int("PORT", 1337),
url: env("PUBLIC_URL", "https://cms.example.com"),
app: {
keys: env.array("APP_KEYS"),
},
});
// config/admin.ts
export default ({ env }) => ({
auth: {
secret: env("ADMIN_JWT_SECRET"),
},
apiToken: {
salt: env("API_TOKEN_SALT"),
},
transfer: {
token: {
salt: env("TRANSFER_TOKEN_SALT"),
},
},
});
Environment Variables
# .env (production)
HOST=0.0.0.0
PORT=1337
PUBLIC_URL=https://cms.example.com
DATABASE_HOST=127.0.0.1
DATABASE_PORT=5432
DATABASE_NAME=strapi_prod
DATABASE_USERNAME=strapi
DATABASE_PASSWORD=SecurePassword123
APP_KEYS=key1,key2,key3,key4
ADMIN_JWT_SECRET=your-admin-jwt-secret
API_TOKEN_SALT=your-api-token-salt
JWT_SECRET=your-jwt-secret
TRANSFER_TOKEN_SALT=your-transfer-token-salt
NODE_ENV=production
Building for Production
# Build admin panel and server
NODE_ENV=production npm run build
# Start production server
NODE_ENV=production npm start
# Or use PM2
npm install -g pm2
pm2 start npm --name "strapi" -- start
pm2 save
pm2 startup
Systemd Service
[Unit]
Description=Strapi CMS
After=network.target postgresql.service
[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/strapi
ExecStart=/usr/bin/npm start
Environment=NODE_ENV=production
EnvironmentFile=/opt/strapi/.env
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
Nginx Reverse Proxy
server {
listen 443 ssl http2;
server_name cms.example.com;
ssl_certificate /etc/letsencrypt/live/cms.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/cms.example.com/privkey.pem;
client_max_body_size 50M;
location / {
proxy_pass http://127.0.0.1:1337;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
S3 Media Upload
# Install S3 provider
npm install @strapi/provider-upload-aws-s3
// config/plugins.ts
export default ({ env }) => ({
upload: {
config: {
provider: "aws-s3",
providerOptions: {
s3Options: {
credentials: {
accessKeyId: env("AWS_ACCESS_KEY_ID"),
secretAccessKey: env("AWS_ACCESS_SECRET"),
},
region: env("AWS_REGION"),
params: {
Bucket: env("AWS_BUCKET"),
},
},
},
},
},
});
Docker Deployment
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN NODE_ENV=production npm run build
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app .
RUN npm ci --production
USER node
EXPOSE 1337
CMD ["npm", "start"]
Summary
Strapi v5 is the most flexible open-source headless CMS, providing a complete admin panel, content API, and plugin system. Deploying on your own VPS gives you full control over data, unlimited API calls, and the ability to customize every aspect. Use PostgreSQL for production reliability, S3 for scalable media storage, and PM2 or systemd for process management. The admin panel is built into the application, so a single deployment serves both the API and the content management interface.