Docs / Programming & Development / Deploy Strapi v5

Deploy Strapi v5

By Admin · Mar 15, 2026 · Updated Apr 23, 2026 · 385 views · 3 min read

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.

Was this article helpful?