How to Self-Host Apostrophe CMS with Docker Compose
What Is Apostrophe CMS?
Apostrophe CMS is an open-source content management framework built with Node.js and MongoDB. Its defining feature is in-context editing — you edit content directly on the page as it appears to visitors, rather than in a separate admin panel. It uses a module-based architecture that supports both traditional server-rendered sites and headless API-driven frontends. Licensed under MIT, making it fully open source for personal and commercial use. Official site.
Prerequisites
- A Linux server (Ubuntu 22.04+ recommended)
- Docker and Docker Compose installed (guide)
- 1 GB of free disk space
- 1 GB of RAM minimum (2 GB recommended)
- Node.js 22+ and npm (for initial project creation)
- A domain name (optional, for remote access)
Docker Compose Configuration
First, create a new Apostrophe project:
npx create-apostrophe-app ~/apostrophe-site
cd ~/apostrophe-site
Create a Dockerfile in the project root:
FROM node:22-alpine
WORKDIR /srv/www/apostrophe
RUN chown -R node:node /srv/www/apostrophe
USER node
COPY --chown=node package*.json ./
ENV NODE_ENV=production
RUN npm ci --production
COPY --chown=node . .
RUN APOS_RELEASE_ID=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) \
&& echo $APOS_RELEASE_ID > ./release-id \
&& node app @apostrophecms/asset:build
EXPOSE 3000
CMD ["node", "app.js"]
Create docker-compose.yml:
services:
apostrophe:
build: .
container_name: apostrophe
ports:
- "3000:3000"
volumes:
- apostrophe_uploads:/srv/www/apostrophe/public/uploads
environment:
- NODE_ENV=production
- APOS_MONGODB_URI=mongodb://apostrophe-db:27017/apostrophe
- APOS_CLUSTER_PROCESSES=2
depends_on:
apostrophe-db:
condition: service_healthy
restart: unless-stopped
apostrophe-db:
image: mongo:8.0
container_name: apostrophe-db
volumes:
- apostrophe_dbdata:/data/db
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
volumes:
apostrophe_uploads:
apostrophe_dbdata:
Build and start:
docker compose build
docker compose up -d
Create the admin user:
docker compose exec apostrophe node app @apostrophecms/user:add admin admin
This creates a user named “admin” with the “admin” role. You’ll be prompted to set a password.
Initial Setup
- Open
http://your-server-ip:3000in your browser - Click the “Log In” button (typically in the admin bar)
- Log in with the admin credentials you created
- The admin bar appears at the top — click “New” to create pages and content
- Edit content directly on the page by clicking any editable region
Configuration
| Setting | Environment Variable | Description |
|---|---|---|
| MongoDB URI | APOS_MONGODB_URI | Connection string for MongoDB |
| Cluster processes | APOS_CLUSTER_PROCESSES | Worker count for Node.js clustering (set to 2 for availability) |
| Node environment | NODE_ENV | production or development |
| External frontend | APOS_EXTERNAL_FRONT_KEY | API key for headless/external frontend access |
| Public host | APOS_HOST | Public URL for the site |
Module Architecture
Apostrophe uses a module-based system. Core modules handle pages, pieces (structured content), widgets, and areas. Custom modules extend functionality:
// modules/article/index.js
module.exports = {
extend: '@apostrophecms/piece-type',
options: {
label: 'Article',
pluralLabel: 'Articles'
},
fields: {
add: {
body: {
type: 'area',
options: {
widgets: {
'@apostrophecms/rich-text': {},
'@apostrophecms/image': {}
}
}
},
publishDate: {
type: 'date',
label: 'Publish Date'
}
}
}
};
Register modules in app.js:
require('apostrophe')({
shortName: 'my-site',
modules: {
article: {}
}
});
S3 Asset Storage
For production deployments, store uploaded assets in S3-compatible storage:
environment:
- APOS_S3_BUCKET=my-bucket
- APOS_S3_REGION=us-east-1
- APOS_S3_KEY=access-key
- APOS_S3_SECRET=secret-key
- APOS_S3_ENDPOINT=https://s3.amazonaws.com
This also works with MinIO, Wasabi, or any S3-compatible provider.
Reverse Proxy
Example Nginx Proxy Manager configuration:
- Scheme:
http - Forward Hostname:
apostrophe - Forward Port:
3000
See Reverse Proxy Setup for full configuration guides.
Backup
Back up these volumes:
apostrophe_dbdata— MongoDB database with all content, users, and configurationapostrophe_uploads— uploaded media files
Use mongodump for consistent backups:
docker compose exec apostrophe-db mongodump --out /data/backup
docker cp apostrophe-db:/data/backup ./mongo-backup-$(date +%Y%m%d)
See Backup Strategy for automated approaches.
Troubleshooting
Admin Bar Not Appearing
Symptom: Site loads but no admin bar or login option visible.
Fix: Navigate to /login directly. Verify the admin user exists: docker compose exec apostrophe node app @apostrophecms/user:list. If no users exist, create one with the user:add command.
Asset Build Errors During Docker Build
Symptom: Dockerfile build fails at the asset build step.
Fix: Ensure APOS_RELEASE_ID is generated before the build command. Check Node.js version compatibility — Apostrophe v4 requires Node.js 18+. Clear the build cache: docker compose build --no-cache.
MongoDB Connection Timeout
Symptom: Apostrophe exits with “MongoServerSelectionError: connect ECONNREFUSED”.
Fix: Ensure MongoDB is fully started before Apostrophe. The depends_on with service_healthy handles this. For external MongoDB, verify the connection string format: mongodb://host:27017/database.
In-Context Editing Not Working
Symptom: Clicking editable areas doesn’t open the editor. Fix: Check browser console for JavaScript errors. Ensure assets were built correctly during the Docker build. If using a CDN or reverse proxy, verify that all Apostrophe asset URLs are accessible.
Resource Requirements
- RAM: ~200 MB idle, ~500 MB under load with clustering
- CPU: Low-Medium — Node.js handles requests efficiently
- Disk: ~150 MB for application, plus media uploads and MongoDB storage
Verdict
Apostrophe CMS stands out with its in-context editing experience — editing content directly on the rendered page is more intuitive than any separate admin panel. The module architecture is clean and extensible, and the MIT license means no licensing costs. The trade-off is ecosystem size: Apostrophe’s plugin and theme library is tiny compared to WordPress or even Ghost. MongoDB as a requirement adds operational complexity compared to flat-file systems like Statamic. Choose Apostrophe if in-context editing is a priority and you’re comfortable with Node.js and MongoDB. For a simpler Node.js CMS with a larger community, Ghost is the safer choice.
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments