This guide provides production-ready Docker Compose configurations for deploying Socioboard with security hardening and best practices.
⚠️ Important: Socioboard 5.0’s last release was in November 2019. The official Docker configuration is intended for development/testing. For production, consider the hardened configuration below or evaluate actively maintained alternatives like Mixpost or Postiz.
# Clone repository
git clone https://github.com/socioboard/Socioboard-5.0.git socioboard
cd socioboard
# Configure environment
cp docker/.env.example docker/.env
nano docker/.env
# Initialize and start
./docker-set-mongo-init.sh
docker compose -f docker/docker-compose.yaml up -d
# Verify
docker logs socioboard
Socioboard provides an official Docker setup in the docker/ directory:
| File | Purpose |
|---|---|
docker/docker-compose.yaml |
Main compose configuration |
docker/.env.example |
Environment template |
docker/README.md |
Docker documentation |
docker-build.sh |
Build script |
docker-set-mongo-init.sh |
MongoDB initialization |
| Service | Image | Port | Purpose |
|---|---|---|---|
socioboard |
Custom build | 80, 443 | Main application |
socioboard-mysql |
MySQL | 3306 | MySQL database |
socioboard-mongo |
MongoDB | 27017 | MongoDB database |
For production deployments, use this hardened configuration with security best practices.
services:
socioboard:
image: ghcr.io/socioboard/socioboard:5.0
container_name: socioboard-app
restart: unless-stopped
depends_on:
mysql:
condition: service_healthy
mongodb:
condition: service_healthy
networks:
- socioboard-internal
expose:
- "8080"
environment:
- NODE_ENV=production
- APP_ENV=production
- APP_URL=https://socioboard.example.com
- DB_HOST=mysql
- DB_PORT=3306
- DB_NAME=socioboard
- DB_USER=socioboard
- DB_PASSWORD=${DB_PASSWORD:?Set DB_PASSWORD in .env}
- MONGO_HOST=mongodb
- MONGO_PORT=27017
- MONGO_DB=socioboard
- MONGO_USER=socioboard
- MONGO_PASSWORD=${MONGO_PASSWORD:?Set MONGO_PASSWORD in .env}
volumes:
- socioboard-data:/var/www/html/storage
- socioboard-logs:/var/log/socioboard
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- CHOWN
- SETGID
- SETUID
read_only: true
tmpfs:
- /tmp:noexec,nosuid,size=512m
- /run:noexec,nosuid,size=64m
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
cpus: '0.5'
memory: 512M
mysql:
image: mysql:8.0
container_name: socioboard-mysql
restart: unless-stopped
networks:
- socioboard-internal
expose:
- "3306"
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:?Set MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=socioboard
- MYSQL_USER=socioboard
- MYSQL_PASSWORD=${DB_PASSWORD:?Set DB_PASSWORD}
volumes:
- mysql-data:/var/lib/mysql
- mysql-backups:/backups
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- CHOWN
- SETGID
- SETUID
- SYS_NICE
read_only: true
tmpfs:
- /tmp:noexec,nosuid,size=256m
- /run:noexec,nosuid,size=64m
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
mongodb:
image: mongo:6.0
container_name: socioboard-mongodb
restart: unless-stopped
networks:
- socioboard-internal
expose:
- "27017"
environment:
- MONGO_INITDB_ROOT_USERNAME=${MONGO_ROOT_USER:?Set MONGO_ROOT_USER}
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_ROOT_PASSWORD:?Set MONGO_ROOT_PASSWORD}
- MONGO_INITDB_DATABASE=socioboard
volumes:
- mongodb-data:/data/db
- mongodb-backups:/backups
- ./docker-init-mongo.js:/docker-entrypoint-initdb.d/init.js:ro
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- CHOWN
- SETGID
- SETUID
- SYS_NICE
read_only: true
tmpfs:
- /tmp:noexec,nosuid,size=256m
- /run:noexec,nosuid,size=64m
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
nginx:
image: nginx:alpine
container_name: socioboard-nginx
restart: unless-stopped
depends_on:
- socioboard
networks:
- socioboard-internal
- socioboard-web
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./ssl:/etc/nginx/ssl:ro
- nginx-cache:/var/cache/nginx
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
read_only: true
tmpfs:
- /tmp:noexec,nosuid,size=128m
- /run:noexec,nosuid,size=64m
- /var/cache/nginx:noexec,nosuid,size=512m
healthcheck:
test: ["CMD", "nginx", "-t"]
interval: 30s
timeout: 10s
retries: 3
networks:
socioboard-internal:
driver: bridge
ipam:
config:
- subnet: 172.28.0.0/16
socioboard-web:
driver: bridge
volumes:
socioboard-data:
socioboard-logs:
mysql-data:
mysql-backups:
mongodb-data:
mongodb-backups:
nginx-cache:
Create a .env file in your project root:
# Database Credentials
DB_PASSWORD=generate-strong-password-here-32chars
MYSQL_ROOT_PASSWORD=generate-strong-password-here-32chars
MONGO_ROOT_USER=mongoroot
MONGO_ROOT_PASSWORD=generate-strong-password-here-32chars
MONGO_PASSWORD=generate-strong-password-here-32chars
# Application Settings
NODE_ENV=production
APP_ENV=production
APP_URL=https://socioboard.example.com
APP_DEBUG=false
APP_KEY=generate-laravel-app-key-here
# Admin Credentials (CHANGE AFTER FIRST LOGIN!)
ADMIN_EMAIL=admin@scb.example
ADMIN_PASSWORD=ChangeMe123!
# Twilio API (Required for SMS verification)
TWILIO_ACCOUNT_SID=
TWILIO_AUTH_KEY=
TWILIO_SERVICE_ID=
# Ports
HTTP_PORT=80
HTTPS_PORT=443
Generate secure passwords:
# Generate random password
openssl rand -base64 32
# Generate Laravel app key
docker run --rm php:8.3-cli php -r "echo 'base64:'.base64_encode(random_bytes(32)).PHP_EOL;"
Create nginx/nginx.conf:
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# Security headers
server_tokens off;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Rate limiting
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=general:10m rate=30r/s;
include /etc/nginx/conf.d/*.conf;
}
Create nginx/conf.d/socioboard.conf:
server {
listen 80;
server_name socioboard.example.com;
# Redirect to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name socioboard.example.com;
# SSL Configuration
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# Modern TLS configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# HSTS
add_header Strict-Transport-Security "max-age=63072000" always;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Rate limiting
limit_req zone=general burst=20 nodelay;
limit_req zone=api burst=10 nodelay;
location / {
proxy_pass http://socioboard:8080;
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_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Block sensitive paths
location ~ /\. {
deny all;
}
location ~ /(\.git|\.env|composer\.(json|lock)) {
deny all;
return 404;
}
}
# Install Docker
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# Install Docker Compose plugin
sudo apt-get install -y docker-compose-plugin
# Verify
docker compose version
# Create directories
mkdir -p socioboard/{nginx/{conf.d,ssl},backups}
cd socioboard
# Clone repository
git clone https://github.com/socioboard/Socioboard-5.0.git src
# Using Let's Encrypt
sudo apt-get install -y certbot
# Generate certificate
sudo certbot certonly --standalone -d socioboard.example.com
# Copy certificates
sudo cp /etc/letsencrypt/live/socioboard.example.com/fullchain.pem nginx/ssl/
sudo cp /etc/letsencrypt/live/socioboard.example.com/privkey.pem nginx/ssl/
sudo chown 101:101 nginx/ssl/*.pem # nginx user ID
# Copy and edit .env
cp src/docker/.env.example .env
nano .env
Create docker-init-mongo.js:
db = db.getSiblingDB('socioboard');
db.createUser({
user: 'socioboard',
pwd: 'your-mongo-password',
roles: [{ role: 'readWrite', db: 'socioboard' }]
});
# Start services
docker compose -f docker-compose.prod.yml up -d
# Check status
docker compose ps
# View logs
docker compose logs -f socioboard
# Start all services
docker compose -f docker-compose.prod.yml up -d
# Stop all services
docker compose -f docker-compose.prod.yml down
# Restart specific service
docker compose -f docker-compose.prod.yml restart socioboard
# View logs
docker compose -f docker-compose.prod.yml logs -f socioboard
docker compose -f docker-compose.prod.yml logs -f mysql
docker compose -f docker-compose.prod.yml logs -f mongodb
# MySQL backup
docker exec socioboard-mysql mysqldump -u root -p${MYSQL_ROOT_PASSWORD} socioboard > backups/mysql_backup_$(date +%Y%m%d).sql
# MongoDB backup
docker exec socioboard-mongodb mongodump --db socioboard --out /backups/mongo_backup_$(date +%Y%m%d)
# MySQL restore
docker exec -i socioboard-mysql mysql -u root -p${MYSQL_ROOT_PASSWORD} socioboard < backups/mysql_backup_20260101.sql
# MongoDB restore
docker exec socioboard-mongodb mongorestore /backups/mongo_backup_20260101
# Pull latest images
docker compose -f docker-compose.prod.yml pull
# Recreate containers
docker compose -f docker-compose.prod.yml up -d --force-recreate
# Cleanup old images
docker image prune -f
Container won’t start:
# Check logs
docker compose logs socioboard
# Inspect container
docker inspect socioboard-app
Database connection errors:
# Test MySQL connection
docker exec -it socioboard-mysql mysql -u socioboard -p
# Test MongoDB connection
docker exec -it socioboard-mongodb mongosh -u socioboard -p
Permission issues:
# Fix volume permissions
docker compose run --rm socioboard chown -R www-data:www-data /var/www/html/storage
Health check failures:
# Check health endpoint
curl -k https://localhost/health
# Review container health
docker compose ps
| Measure | Implementation |
|---|---|
| Non-root user | Containers run as non-root where possible |
| Capability dropping | cap_drop: ALL with minimal additions |
| Read-only filesystem | read_only: true with tmpfs mounts |
| Resource limits | CPU and memory limits configured |
| Network isolation | Internal networks for databases |
| No new privileges | security_opt: no-new-privileges |
Any questions?
Feel free to contact us. Find all contact information on our contact page.