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@latest 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
Command Description
pm2 status Show running processes
pm2 logs wollycms Tail logs
pm2 restart wollycms Restart the server
pm2 stop wollycms Stop the server
Variable Default Description
NODE_ENV development Set to production for production
PORT 4321 Server port
HOST localhost Bind address (0.0.0.0 for all interfaces)
DATABASE_URL sqlite:./data/wolly.db SQLite path or PostgreSQL URL
MEDIA_STORAGE local local, s3, or r2
MEDIA_DIR ./uploads Local storage directory
JWT_SECRET required JWT signing secret
CORS_ORIGINS * Comma-separated allowed origins
SITE_URL http://localhost:4322 Frontend URL for sitemaps/OG images
S3_ENDPOINT - S3-compatible endpoint URL
S3_BUCKET - S3 bucket name
S3_REGION auto S3 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/