Self-Hosting
Self-Hosting
Run your own Manacore instance using Docker Compose.
Requirements
- Docker 24.0+
- Docker Compose v2.0+
- 4GB RAM minimum (8GB recommended)
- 20GB disk space minimum
- Domain name (optional, for HTTPS)
Quick Start
-
Clone the repository
Terminal window git clone https://github.com/manacore/manacore-monorepo.gitcd manacore-monorepo -
Create environment file
Terminal window cp .env.example .env.productionEdit
.env.productionwith your settings:# DatabasePOSTGRES_USER=manacorePOSTGRES_PASSWORD=your-secure-password# RedisREDIS_PASSWORD=your-redis-password# JWT (generate new keys!)JWT_PRIVATE_KEY=your-private-keyJWT_PUBLIC_KEY=your-public-key# DomainDOMAIN=your-domain.com -
Start services
Terminal window docker compose -f docker-compose.production.yml up -d -
Verify deployment
Terminal window # Check servicesdocker compose -f docker-compose.production.yml ps# Check healthcurl http://localhost:3001/health
Configuration
docker-compose.production.yml
version: '3.8'
services: # Database postgres: image: postgres:16-alpine environment: POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - postgres_data:/var/lib/postgresql/data - ./docker/init-db:/docker-entrypoint-initdb.d restart: unless-stopped healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"] interval: 10s timeout: 5s retries: 5
# Cache redis: image: redis:7-alpine command: redis-server --requirepass ${REDIS_PASSWORD} volumes: - redis_data:/data restart: unless-stopped
# Object Storage minio: image: minio/minio command: server /data --console-address ":9001" environment: MINIO_ROOT_USER: ${S3_ACCESS_KEY} MINIO_ROOT_PASSWORD: ${S3_SECRET_KEY} volumes: - minio_data:/data restart: unless-stopped
# Auth Service mana-auth: image: ghcr.io/manacore/mana-core-auth:latest environment: DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/manacore REDIS_HOST: redis REDIS_PASSWORD: ${REDIS_PASSWORD} JWT_PRIVATE_KEY: ${JWT_PRIVATE_KEY} JWT_PUBLIC_KEY: ${JWT_PUBLIC_KEY} depends_on: postgres: condition: service_healthy redis: condition: service_started restart: unless-stopped
# Chat Backend chat-backend: image: ghcr.io/manacore/chat-backend:latest environment: DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/chat MANA_CORE_AUTH_URL: http://mana-auth:3001 JWT_PUBLIC_KEY: ${JWT_PUBLIC_KEY} depends_on: - mana-auth restart: unless-stopped
volumes: postgres_data: redis_data: minio_data:Generate JWT Keys
Manacore uses EdDSA (Ed25519) for JWT signing:
# Generate private keyopenssl genpkey -algorithm ed25519 -out private.pem
# Extract public keyopenssl pkey -in private.pem -pubout -out public.pem
# Convert to base64 for .envecho "JWT_PRIVATE_KEY=$(cat private.pem | base64 -w 0)"echo "JWT_PUBLIC_KEY=$(cat public.pem | base64 -w 0)"Reverse Proxy Setup
Nginx
server { listen 80; server_name api.your-domain.com; return 301 https://$server_name$request_uri;}
server { listen 443 ssl http2; server_name api.your-domain.com;
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
location / { proxy_pass http://localhost:3001; 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; }}Caddy
api.your-domain.com { reverse_proxy localhost:3001}
chat-api.your-domain.com { reverse_proxy localhost:3002}SSL with Let’s Encrypt
# Install certbotapt install certbot python3-certbot-nginx
# Get certificatescertbot --nginx -d api.your-domain.com -d chat-api.your-domain.com
# Auto-renewal (added automatically)certbot renew --dry-runBackup Strategy
Daily Database Backup
Create /etc/cron.daily/manacore-backup:
#!/bin/bashBACKUP_DIR=/var/backups/manacoreDATE=$(date +%Y%m%d)
# Create backup directorymkdir -p $BACKUP_DIR
# Backup all databasesdocker exec postgres pg_dumpall -U manacore > $BACKUP_DIR/all_$DATE.sql
# Compressgzip $BACKUP_DIR/all_$DATE.sql
# Keep last 7 daysfind $BACKUP_DIR -name "*.gz" -mtime +7 -deleteRestore from Backup
# Stop servicesdocker compose -f docker-compose.production.yml stop
# Restoregunzip -c /var/backups/manacore/all_20240115.sql.gz | docker exec -i postgres psql -U manacore
# Start servicesdocker compose -f docker-compose.production.yml startUpdating
-
Pull latest images
Terminal window docker compose -f docker-compose.production.yml pull -
Backup database (always!)
Terminal window docker exec postgres pg_dumpall -U manacore > backup_before_update.sql -
Restart services
Terminal window docker compose -f docker-compose.production.yml up -d -
Verify
Terminal window docker compose -f docker-compose.production.yml pscurl http://localhost:3001/health
Troubleshooting
Services Not Starting
# Check logsdocker compose -f docker-compose.production.yml logs
# Check specific servicedocker compose -f docker-compose.production.yml logs mana-authDatabase Connection Failed
# Verify postgres is runningdocker compose -f docker-compose.production.yml ps postgres
# Check connectiondocker exec -it postgres psql -U manacore -c "SELECT 1"Out of Memory
Increase Docker memory limits or add swap:
# Add 2GB swapfallocate -l 2G /swapfilechmod 600 /swapfilemkswap /swapfileswapon /swapfileecho '/swapfile none swap sw 0 0' >> /etc/fstab