← Blog

Auto-hospedar MediaCMS para hospedaje de video

Railyard

16 de septiembre de 2026 · 8 min de lectura

Sigue esto en tu propio servidor. Empieza gratis, sin tarjeta

Correr tu propio portal de video normalmente significa elegir entre un envoltorio delgado sobre la factura de almacenamiento de otro y un verdadero CMS de medios que maneja subidas, transcodificación y reproducción por sí mismo. MediaCMS es del segundo tipo: una app Django de código abierto que recibe video, audio e imágenes, los transcodifica a varias resoluciones con workers Celery corriendo en segundo plano, y sirve un portal con marca propia con su propia búsqueda y listas de reproducción. Al final de esta guía tendrás la app web, un worker de transcodificación separado, Postgres y Redis corriendo juntos detrás de HTTPS, con respaldos y una subida real probada de principio a fin.

Lo que necesitas

  • Un servidor con Docker y el plugin de Compose. Un Hetzner CX33 (4 vCPU, 8 GB de RAM, 80 GB NVMe, 8,99 €/mes sin IVA) es el piso realista en cuanto entra en juego la transcodificación; un CX23 técnicamente corre la app web, pero las subidas simultáneas compiten con el manejo de solicitudes equivalente a Puma por la misma CPU limitada, que un proceso worker dedicado necesita poder usar.
  • Un nombre de dominio al que puedas apuntar un registro A.
  • Una planificación real de disco: MediaCMS guarda la subida original más cada resolución transcodificada más los segmentos HLS para streaming, una regla general aproximada del propio proyecto es tres veces el tamaño de lo que realmente subes, a lo largo de un año de subidas a tu ritmo esperado.
  • Acceso SSH al servidor.

Paso 1: apuntar el DNS y abrir el firewall

ufw allow OpenSSH
ufw allow 80
ufw allow 443
ufw --force enable

Crea un registro A para tu dominio apuntando a la IP pública del servidor, y dale unos minutos antes del Paso 5.

Paso 2: generar los secretos

openssl rand -hex 24   # POSTGRES_PASSWORD
openssl rand -hex 24   # ADMIN_PASSWORD
openssl rand -hex 32   # SECRET_KEY
mkdir -p /opt/mediacms
cat > /opt/mediacms/.env <<'EOF'
POSTGRES_PASSWORD=paste-the-first-value-here
ADMIN_PASSWORD=paste-the-second-value-here
SECRET_KEY=paste-the-third-value-here
EOF
chmod 600 /opt/mediacms/.env

SECRET_KEY importa más aquí que en una app Django típica: si se deja sin definir, el propio local_settings.py de MediaCMS vuelve a un valor por defecto fijo incluido en cada copia de la imagen, lo que dejaría que cualquiera falsifique cookies firmadas o tokens de restablecimiento de contraseña contra cualquier despliegue que la haya dejado sin definir. Definirla explícitamente, y compartir el mismo valor entre los procesos web y worker, no es opcional.

Paso 3: escribir el archivo Compose

MediaCMS en realidad necesita cuatro procesos que leen todos el mismo entorno: la app web, un worker Celery para transcodificación, un programador Celery beat para tareas periódicas, y la base de datos y el caché que comparten.

services:
  mediacms:
    image: mediacms/mediacms:7.2.0
    restart: unless-stopped
    env_file: .env
    command: ./deploy/docker/prestart.sh
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    environment:
      FRONTEND_HOST: https://videos.yourdomain.com
      REDIS_LOCATION: redis://redis:6379/1
      POSTGRES_HOST: db
      POSTGRES_NAME: mediacms
      POSTGRES_USER: mediacms
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      ENABLE_CELERY_SHORT: "no"
      ENABLE_CELERY_LONG: "no"
      ENABLE_CELERY_BEAT: "no"
      ADMIN_USER: admin
      ADMIN_EMAIL: admin@yourdomain.com
      ADMIN_PASSWORD: ${ADMIN_PASSWORD}
      SECRET_KEY: ${SECRET_KEY}
    volumes:
      - mediacms_media:/home/mediacms.io/mediacms/media_files
    expose:
      - "80"

  worker:
    image: mediacms/mediacms:7.2.0
    restart: unless-stopped
    env_file: .env
    depends_on:
      - mediacms
    environment:
      REDIS_LOCATION: redis://redis:6379/1
      POSTGRES_HOST: db
      POSTGRES_NAME: mediacms
      POSTGRES_USER: mediacms
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      SECRET_KEY: ${SECRET_KEY}
      ENABLE_UWSGI: "no"
      ENABLE_NGINX: "no"
      ENABLE_CELERY_BEAT: "no"
      ENABLE_MIGRATIONS: "no"
    volumes:
      - mediacms_media:/home/mediacms.io/mediacms/media_files

  beat:
    image: mediacms/mediacms:7.2.0
    restart: unless-stopped
    env_file: .env
    depends_on:
      - mediacms
    environment:
      REDIS_LOCATION: redis://redis:6379/1
      POSTGRES_HOST: db
      POSTGRES_NAME: mediacms
      POSTGRES_USER: mediacms
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      SECRET_KEY: ${SECRET_KEY}
      ENABLE_UWSGI: "no"
      ENABLE_NGINX: "no"
      ENABLE_CELERY_SHORT: "no"
      ENABLE_CELERY_LONG: "no"
      ENABLE_MIGRATIONS: "no"

  db:
    image: postgres:17.2-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: mediacms
      POSTGRES_DB: mediacms
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - mediacms_db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U mediacms"]
      interval: 5s
      timeout: 5s
      retries: 10

  redis:
    image: redis:7.2.5-alpine
    restart: unless-stopped
    volumes:
      - mediacms_redis_data:/data

  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      - mediacms

volumes:
  mediacms_media:
  mediacms_db_data:
  mediacms_redis_data:
  caddy_data:
  caddy_config:

Esta guía fija 7.2.0, la versión estable vigente al momento de escribir esto. El servicio mediacms corre prestart.sh, el propio script de entrada de MediaCMS que aplica migraciones y arranca el servidor web real, por eso ENABLE_MIGRATIONS se deja sin definir ahí pero explícitamente en "no" en worker y beat, correr migraciones desde tres procesos a la vez en cada arranque es una forma real de toparse con una condición de carrera. ENABLE_CELERY_SHORT, ENABLE_CELERY_LONG y ENABLE_CELERY_BEAT están todos en "no" en el servicio mediacms específicamente para que el contenedor web no intente también correr Celery por su cuenta; esos tres roles se reparten entre worker y beat en su lugar, cada uno desactivando el rol que no le corresponde a su vez. mediacms_media es un único volumen compartido entre mediacms y worker, ya que el worker lee lo que el proceso web aceptó y escribe la salida transcodificada que ambos necesitan ver.

Verifica que el archivo se analiza:

docker compose config

Paso 4: agregar Caddy y arrancar la pila

videos.yourdomain.com {
	reverse_proxy mediacms:80
}
docker compose up -d
docker compose ps
curl -I https://videos.yourdomain.com

Deberías ver cada servicio en Up (db como healthy) y el curl devolver HTTP/2 200.

Paso 5: iniciar sesión y subir un video de prueba

Visita https://videos.yourdomain.com e inicia sesión con admin y el ADMIN_PASSWORD del Paso 2. Sube un video corto de prueba desde la interfaz, y luego revisa que realmente se haya transcodificado, no solo subido:

docker compose logs -f worker

Deberías ver la salida de tareas de Celery referenciando el ID del nuevo medio, terminando en un estado completado, y el video mismo debería poder reproducirse en el navegador uno o dos minutos después una vez que el worker se ponga al día, más tiempo para un archivo más grande.

Paso 6: respaldar Postgres y el volumen de medios

cat > /opt/mediacms/backup.sh <<'EOF'
#!/bin/sh
cd /opt/mediacms
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
docker compose exec -T db pg_dump -U mediacms mediacms | gzip > backups/db-$TIMESTAMP.sql.gz
find backups -mtime +14 -delete
EOF
mkdir -p /opt/mediacms/backups
chmod +x /opt/mediacms/backup.sh
(crontab -l 2>/dev/null; echo "0 3 * * * /opt/mediacms/backup.sh") | crontab -

El dump de la base de datos cubre metadatos, cuentas y listas de reproducción, no los archivos de video reales en mediacms_media, que casi con seguridad es la parte más grande y valiosa de esta instalación. Respalda ese volumen con un snapshot de disco completo de tu proveedor de servidor, o un rsync programado a otra máquina; correr un tar sobre medios en vivo mientras hay subidas en curso arriesga un archivo inconsistente, así que prográmalo para un período tranquilo de la misma forma en que el Paso 6 de otras guías maneja una base de datos activa.

Paso 7: actualizar

docker compose pull
docker compose up -d

El prestart.sh del servicio mediacms vuelve a aplicar migraciones automáticamente al reiniciar; worker y beat también reciben la nueva imagen ya que la comparten, pero se saltan las migraciones intencionalmente, como se definió en el Paso 3. Revisa docker compose logs mediacms en busca de un arranque limpio antes de dar por terminada la actualización.

El atajo: hazlo con Railyard

Todo lo anterior, Postgres y Redis conectados a los tres procesos, los roles de worker y beat correctamente separados para que no corran Celery dos veces ni compitan en las migraciones, un volumen de medios compartido, es también lo que configura por ti la plantilla de pila MediaCMS de Railyard. Conecta un servidor, y luego elige MediaCMS en el catálogo de plantillas: app.railyard.run/deploy?stack=mediacms. SECRET_KEY y ADMIN_PASSWORD se generan por ti en lugar de dejarse como marcadores de posición, la contraseña de administrador generada aparece en el panel de la app en lugar de imprimirse una sola vez en un log como hace el propio prestart.sh de MediaCMS cuando se deja sin definir.

Consulta desplegar con un botón y bases de datos y copias de seguridad para ver cómo funcionan las plantillas y los datos persistentes en la plataforma de forma más general.

Solución de problemas

Las subidas tienen éxito pero los videos nunca terminan de procesarse. Revisa primero docker compose logs worker; un worker que está en bucle de fallos o que se cerró por falta de memoria se manifiesta como el contenedor reiniciándose en lugar de un error claro en el log, en cuyo caso la solución es más memoria o CPU para el servidor, no un cambio de configuración.

El inicio de sesión de administrador rechaza una contraseña de la que estás seguro que es correcta. Confirma que ADMIN_PASSWORD en .env coincida exactamente con lo que estás escribiendo; si la pila ya estaba corriendo antes de que la definieras, la cuenta se creó con el valor que estaba presente en el primer arranque, cámbiala desde el panel de administración de Django en lugar de volver a editar .env después del hecho.

Las migraciones parecen correr dos veces o entrar en conflicto al arrancar. Confirma que ENABLE_MIGRATIONS: "no" realmente esté presente tanto en worker como en beat en tu archivo compose; si falta en cualquiera de los dos, verás errores de migración intermitentes que parecen no estar relacionados con la causa real.

El HTTPS no se emite. Que docker compose logs caddy muestre errores de ACME repetidos casi siempre significa que el DNS del Paso 1 no se ha propagado o apunta a la IP equivocada.

El disco se llena mucho más rápido de lo esperado. MediaCMS guarda la subida original, las versiones transcodificadas, y los segmentos HLS, todos juntos; du -sh contra el volumen de medios muestra el uso real, y la regla general de tres veces el tamaño de la subida de “Lo que necesitas” vale la pena revisarla contra tu propio ritmo real de subidas en lugar de asumirla una vez y olvidarla.

Preguntas frecuentes

¿Puedo desactivar la transcodificación y simplemente servir el archivo original? No de forma significativa, el streaming adaptativo (servir la resolución correcta según la conexión del espectador) es una función central, no un complemento; si solo necesitas hospedar un puñado de archivos ya codificados sin transcodificar, un simple hospedaje de archivos estáticos encaja mejor que MediaCMS.

¿MediaCMS necesita un proveedor SMTP? Solo para funciones opcionales como notificaciones por correo en comentarios; tanto la creación de cuentas como el hospedaje de video central funcionan sin él, menos estricto que apps que requieren SMTP para confirmar una cuenta nueva.

¿El worker puede correr en un servidor separado, más barato, que el proceso web? Sí, mientras pueda llegar al mismo Postgres, Redis y almacenamiento de medios compartido, separar el worker en su propia máquina es un siguiente paso razonable en cuanto la carga de transcodificación realmente compite con el tráfico web por CPU.

¿Qué formatos de video acepta? MediaCMS acepta formatos comunes como MP4, MOV, MKV, AVI, WebM y más al subir, y los transcodifica a una salida estándar apta para la web sin importar el formato de origen.

¿Hay un límite de tamaño en las subidas? El valor por defecto del propio MediaCMS es generoso pero configurable; el espacio en disco de un servidor pequeño es el límite más práctico en una instalación auto-hospedada que cualquier cosa que MediaCMS imponga por defecto.

Una vez que Postgres, Redis y el worker están todos conectados con los mismos secretos, subir y transcodificar un video son los mismos pocos clics que serían en una plataforma gestionada. El trabajo continuo real es el hábito de respaldo del Paso 6 y mantener el crecimiento del disco por delante de lo que el servidor realmente tiene.

Por qué eligen Railyard para esto

  • Tu propia cuenta en la nube. Despliega en DigitalOcean, Hetzner, Vultr, AWS, OVHcloud, o cualquier servidor Linux por SSH, facturado directamente por tu proveedor, sin márgenes adicionales.
  • Un precio fijo por equipo. El plan Free es gratis para siempre: un servidor más un servidor de base de datos, tres apps, sin tarjeta. Pro cuesta $19 al mes por hasta cinco servidores y apps ilimitadas. Team cuesta $49 al mes con servidores ilimitados. Sin cargos por app, por despliegue, por asiento ni por gigabyte.
  • Plantillas de un clic. Despliega apps de código abierto, como bases de datos, analítica y herramientas de desarrollo, en tu propio servidor con un clic.
  • Bases de datos administradas. Postgres, MySQL y Redis, con copias de seguridad programadas y verificadas por restauración, y recuperación a un punto en el tiempo.
  • Despliegue con git push. Despliega desde GitHub, GitLab o la CLI, con HTTPS automático y lanzamientos sin tiempo de inactividad.

Despliega MediaCMS en un clic

Elige MediaCMS en el catálogo de plantillas de Railyard y obtén Postgres, Redis y el worker de transcodificación ya conectados.

Plan gratis, sin tarjeta, y 14 días de Pro para cada equipo nuevo. Un precio fijo por equipo.