Skip to content

Node.js

Run WollyCMS directly on a Node.js server for maximum control and simplicity.

  • Node.js 22 or later
  • npm 10 or later
Terminal window
npx create-wolly my-cms
cd my-cms
npm install

Create a .env file (the scaffold generates one for you):

Terminal window
NODE_ENV=production
PORT=4321
HOST=0.0.0.0
DATABASE_URL=sqlite:./data/wolly.db
MEDIA_DIR=./uploads
JWT_SECRET=your-strong-random-secret
CORS_ORIGINS=https://your-site.example.com
SITE_URL=https://your-site.example.com

Generate a JWT secret:

Terminal window
openssl rand -base64 32
Terminal window
npm run migrate
npm run seed
Terminal window
npm start

The CMS is now running at http://localhost:4321.

PM2 keeps the process running, handles restarts, and manages logs.

Terminal window
npm install -g pm2

Create ecosystem.config.cjs:

module.exports = {
apps: [{
name: 'wollycms',
script: 'node_modules/@wollycms/server/dist/index.js',
env: {
NODE_ENV: 'production',
PORT: 4321,
HOST: '0.0.0.0',
},
instances: 1,
autorestart: true,
max_memory_restart: '512M',
log_date_format: 'YYYY-MM-DD HH:mm:ss',
}],
};
Terminal window
pm2 start ecosystem.config.cjs
pm2 save
pm2 startup # Auto-start on boot
CommandDescription
pm2 statusShow running processes
pm2 logs wollycmsTail logs
pm2 restart wollycmsRestart the server
pm2 stop wollycmsStop the server
VariableDefaultDescription
NODE_ENVdevelopmentSet to production for production
PORT4321Server port
HOSTlocalhostBind address (0.0.0.0 for all interfaces)
DATABASE_URLsqlite:./data/wolly.dbSQLite path or PostgreSQL URL
MEDIA_STORAGElocallocal, s3, or r2
MEDIA_DIR./uploadsLocal storage directory
JWT_SECRETrequiredJWT signing secret
CORS_ORIGINS*Comma-separated allowed origins
SITE_URLhttp://localhost:4322Frontend URL for sitemaps/OG images
S3_ENDPOINT-S3-compatible endpoint URL
S3_BUCKET-S3 bucket name
S3_REGIONautoS3 region
S3_ACCESS_KEY-S3 access key
S3_SECRET_KEY-S3 secret key
S3_PUBLIC_URL-Public CDN URL for S3 media

Set DATABASE_URL to a PostgreSQL connection string:

Terminal window
DATABASE_URL=postgresql://wolly:password@localhost:5432/wollycms

WollyCMS auto-detects the database type from the URL prefix (sqlite:, postgresql://, or postgres://).

In production, put WollyCMS behind a reverse proxy for TLS termination.

cms.example.com {
reverse_proxy localhost:4321
}
server {
listen 443 ssl;
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:4321;
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;
}
}
my-cms/
├── data/ # SQLite database (back this up!)
├── uploads/ # Media files (back this up!)
├── node_modules/
├── .env # Environment variables
└── package.json

Back up these two directories regularly:

  • data/ — the SQLite database
  • uploads/ — media files (unless using S3/R2)
Terminal window
# Example: daily backup with rsync
rsync -a data/ /backup/wollycms/data/
rsync -a uploads/ /backup/wollycms/uploads/