How to Self-Host MediaCMS with Docker Compose
What Is MediaCMS?
MediaCMS is a self-hosted video and media CMS built with Django and React. Think of it as your own YouTube — users can upload videos, the platform transcodes them to multiple resolutions, generates HLS streams for adaptive playback, and serves them through a modern web interface. It also handles images, audio, and PDFs.
MediaCMS supports multi-user accounts with permissions, categories, tags, playlists, comments, and an admin dashboard. It’s designed for organizations that need a private video platform — universities, companies, media teams, or anyone who wants video hosting without YouTube’s tracking and terms of service.
Prerequisites
- A Linux server (Ubuntu 22.04+ recommended)
- Docker and Docker Compose installed (guide)
- 4 GB of RAM minimum (transcoding is memory-intensive)
- 2+ CPU cores (more is better for video transcoding)
- Sufficient storage for video files (plan for 3x original file size due to transcoding)
- A domain name (optional, for remote access)
Docker Compose Configuration
MediaCMS uses 6 services: database initialization (runs once), web application, Celery beat scheduler, Celery worker (handles transcoding), PostgreSQL, and Redis.
Create a docker-compose.yml file:
services:
migrations:
image: mediacms/mediacms:7.6.0
command: ./deploy/docker/prestart.sh
restart: "no"
depends_on:
redis:
condition: service_healthy
db:
condition: service_healthy
environment:
ENABLE_UWSGI: "no"
ENABLE_NGINX: "no"
ENABLE_CELERY_BEAT: "no"
ENABLE_CELERY_SHORT: "no"
ENABLE_CELERY_LONG: "no"
ENABLE_MIGRATIONS: "yes"
volumes:
- mediacms-data:/home/mediacms.io/mediacms
web:
image: mediacms/mediacms:7.6.0
restart: unless-stopped
ports:
- "80:80"
depends_on:
migrations:
condition: service_completed_successfully
environment:
ENABLE_UWSGI: "yes"
ENABLE_NGINX: "yes"
ENABLE_CELERY_BEAT: "no"
ENABLE_CELERY_SHORT: "no"
ENABLE_CELERY_LONG: "no"
ENABLE_MIGRATIONS: "no"
volumes:
- mediacms-data:/home/mediacms.io/mediacms
celery_beat:
image: mediacms/mediacms:7.6.0
restart: unless-stopped
depends_on:
redis:
condition: service_healthy
environment:
ENABLE_UWSGI: "no"
ENABLE_NGINX: "no"
ENABLE_CELERY_BEAT: "yes"
ENABLE_CELERY_SHORT: "no"
ENABLE_CELERY_LONG: "no"
ENABLE_MIGRATIONS: "no"
volumes:
- mediacms-data:/home/mediacms.io/mediacms
celery_worker:
image: mediacms/mediacms:7.6.0
restart: unless-stopped
depends_on:
migrations:
condition: service_completed_successfully
environment:
ENABLE_UWSGI: "no"
ENABLE_NGINX: "no"
ENABLE_CELERY_BEAT: "no"
ENABLE_CELERY_SHORT: "yes"
ENABLE_CELERY_LONG: "yes"
ENABLE_MIGRATIONS: "no"
volumes:
- mediacms-data:/home/mediacms.io/mediacms
db:
image: postgres:17.2-alpine
restart: unless-stopped
environment:
POSTGRES_USER: mediacms
POSTGRES_PASSWORD: changeme_strong_password
POSTGRES_DB: mediacms
volumes:
- mediacms-db:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U mediacms"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- mediacms-redis:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
volumes:
mediacms-data:
mediacms-db:
mediacms-redis:
Start the stack:
docker compose up -d
Wait 1-2 minutes for the migrations service to complete database initialization, then access the web interface.
Initial Setup
- Access the web interface at
http://your-server-ip. - Log in as admin with the default credentials:
admin/admin. Change this password immediately. - Configure the site name in the Django admin panel at
/admin/. - Set the
FRONTEND_HOSTto your actual domain or IP in the configuration file (see Configuration below).
Configuration
MediaCMS uses Django settings stored in the container. To customize, create a local_settings.py override:
# Copy the default settings template
docker compose exec web cat /home/mediacms.io/mediacms/deploy/docker/local_settings.py > local_settings.py
Key settings to modify:
# Site URL — set to your actual domain
FRONTEND_HOST = "https://media.example.com"
# Site name displayed in the UI
PORTAL_NAME = "My Media Platform"
# Who can upload media: 'all', 'verified_email', or 'admins'
CAN_ADD_MEDIA = "verified_email"
# Maximum uploads per user
NUMBER_OF_MEDIA_USER_CAN_UPLOAD = 100
# Maximum file size in MB
UPLOAD_MAX_SIZE = 4000
# Disable transcoding to save CPU (videos play in original format only)
# DO_NOT_TRANSCODE_VIDEO = True
# Django secret key — generate a unique one for production
SECRET_KEY = "your-random-secret-key-here"
# Debug mode — always False in production
DEBUG = False
Mount the custom settings file by adding to the volumes of web, celery_beat, and celery_worker:
volumes:
- mediacms-data:/home/mediacms.io/mediacms
- ./local_settings.py:/home/mediacms.io/mediacms/deploy/docker/local_settings.py:ro
Advanced Configuration (Optional)
Whisper Subtitle Generation
Use the -full image variant for automatic speech-to-text subtitles:
web:
image: mediacms/mediacms:7.6.0-full # 4.7 GB — includes Whisper
This adds OpenAI Whisper for automatic subtitle generation on uploaded videos. The full image is 4.7 GB (vs 509 MB standard) and requires additional RAM (2+ GB extra) and CPU.
Email Configuration
For user registration verification emails, add SMTP settings to local_settings.py:
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.example.com"
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = "[email protected]"
EMAIL_HOST_PASSWORD = "your-password"
DEFAULT_FROM_EMAIL = "[email protected]"
Disable Transcoding
If your server lacks CPU power for transcoding, disable it:
DO_NOT_TRANSCODE_VIDEO = True
Videos will play in their original format. This saves significant CPU but means no adaptive streaming (HLS) and no multi-resolution playback.
Reverse Proxy
The web service includes Nginx internally on port 80. To add HTTPS, place a reverse proxy in front of it.
For Nginx Proxy Manager, create a proxy host pointing to port 80 with SSL enabled.
Important: If your reverse proxy terminates SSL, ensure the FRONTEND_HOST setting uses https:// to prevent mixed content issues.
See Reverse Proxy Setup for detailed instructions.
Backup
Back up three things:
- Media files: The
mediacms-datavolume contains all uploaded media, transcoded versions, and thumbnails. - Database: Contains user accounts, metadata, comments, and settings.
- Configuration: Your
local_settings.pyfile.
# Database backup
docker compose exec db pg_dump -U mediacms mediacms > mediacms-db-backup.sql
# Media files are in the named volume — back up with:
docker run --rm -v mediacms-data:/data -v $(pwd):/backup alpine tar czf /backup/mediacms-data.tar.gz -C /data .
See Backup Strategy for automated approaches.
Troubleshooting
Videos Stuck in “Processing”
Symptom: Uploaded videos show “Processing” indefinitely.
Fix: Check the Celery worker: docker compose logs celery_worker. If it’s not running or shows errors, the transcoding pipeline is broken. Ensure ENABLE_CELERY_LONG: "yes" is set. Restart: docker compose restart celery_worker
Upload Fails for Large Files
Symptom: Uploads fail for files over a certain size.
Fix: The internal Nginx limits uploads to 5.8 GB by default. Check that UPLOAD_MAX_SIZE in Django settings matches or is lower than the Nginx client_max_body_size. If using an external reverse proxy, set its upload limit to match.
Admin Panel Returns 500 Error
Symptom: /admin/ returns a server error.
Fix: Check migrations completed: docker compose logs migrations. If the migrations service failed, the database schema is incomplete. Remove the database volume and restart: docker compose down -v && docker compose up -d
ImageMagick Sprite Generation Errors
Symptom: Thumbnail sprite generation fails with “width or height exceeds limit” in celery_worker logs. Fix: This is an ImageMagick security policy limitation for very long videos. The thumbnails still generate — sprite previews for the timeline scrubber may be missing for videos over 4 hours.
Default Admin Password Not Working
Symptom: Can’t log in with admin/admin.
Fix: The default admin is created during the migrations step. If you reset the database, the migrations service needs to run again. Check: docker compose logs migrations | grep -i admin
Resource Requirements
- RAM: 1 GB idle (web + Celery + PostgreSQL + Redis), 2-4 GB during video transcoding
- CPU: 2 cores minimum, 4+ cores recommended for concurrent transcoding
- Disk: 500 MB for application, plus 3x storage for each GB of uploaded video (original + transcoded + HLS segments)
Verdict
MediaCMS is the best self-hosted YouTube-like platform. It handles the full video pipeline — upload, transcode, stream, manage — in a single Docker Compose stack. The multi-user support, automatic transcoding, and HLS streaming make it suitable for organizations that need private video hosting.
The resource requirements are real — video transcoding is CPU-intensive and storage multiplies fast. For personal video hosting with lighter requirements, consider Jellyfin (which plays files as-is without transcoding overhead). For YouTube content archival rather than hosting, look at Tube Archivist.
FAQ
How does MediaCMS compare to Jellyfin for video hosting?
Different use cases. MediaCMS is a video hosting platform (upload, transcode, share publicly) — like a private YouTube. Jellyfin is a media library (organize and stream existing files) — like a private Netflix. MediaCMS transcodes uploads into multiple resolutions with HLS streaming; Jellyfin plays files as-is or transcodes on demand. Choose MediaCMS for user-uploaded content; choose Jellyfin for a personal media library.
Can MediaCMS handle live streaming?
No. MediaCMS handles video-on-demand (upload, transcode, stream). For live streaming, use MediaMTX (RTSP/RTMP/HLS proxy) or a dedicated streaming platform. You can record live streams with MediaMTX and upload the recordings to MediaCMS for on-demand viewing.
How much disk space do transcoded videos use?
Roughly 3x the original file size. A 1 GB upload generates ~3 GB total: the original file, transcoded versions at multiple resolutions, HLS segments, and thumbnails. Plan your storage accordingly — a 500 GB disk holds approximately 150 GB of original uploads.
Does MediaCMS support user registration and permissions?
Yes. MediaCMS supports multi-user accounts with configurable permissions: admin, manager, editor, and viewer roles. You can allow public registration, require admin approval, or restrict to invite-only. Each user gets their own upload space and channel.
Can I disable video transcoding?
Yes. Set DO_NOT_TRANSCODE_VIDEO = True in local_settings.py. Videos play in their original format, saving CPU. The downside is no adaptive streaming (HLS) and no multi-resolution playback — viewers need enough bandwidth for the original quality.
What’s the difference between the standard and full Docker images?
The standard image (~509 MB) handles all core functionality. The -full image (~4.7 GB) adds OpenAI Whisper for automatic subtitle generation from speech. Use the standard image unless you need automatic subtitles — the full image requires significantly more RAM and disk space.
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