Auto-hospedar un servidor Mastodon
Sigue esto en tu propio servidor. Empieza gratis, sin tarjeta
Correr tu propio rincón del fediverso significa correr cinco cosas que todas tienen que estar de acuerdo entre sí: un proceso web Rails, un servidor de streaming separado para la línea de tiempo en tiempo real, un worker Sidekiq para la federación y las notificaciones, Postgres, y Redis. Si te equivocas con el dominio en cualquiera de ellos, obtienes una instancia que carga a medias o que deja de federar en silencio. Al final de esta guía tendrás una instancia real de Mastodon en un solo servidor corriendo las cinco, detrás de HTTPS, con respaldos y una mirada honesta a lo que realmente cuesta correrla bien.
Lo que necesitas
- Un servidor con al menos 4 vCPU y 8 GB de RAM para una instancia pequeña; esta guía usa los cinco servicios en una sola máquina, que es más de lo que necesita el proceso web por sí solo. Un Hetzner CX33 (4 vCPU, 8 GB de RAM, 80 GB NVMe, 8,99 €/mes sin IVA) es el punto de partida realista una vez que Postgres, Redis, el servidor de streaming y Sidekiq compiten todos por recursos en la misma máquina; crecer más allá de unos pocos cientos de usuarios activos es cuando separar Postgres en su propia máquina empieza a importar.
- Un nombre de dominio al que puedas apuntar un registro A. Mastodon ata los identificadores de cuenta a este dominio de forma permanente; no hay una forma admitida de mover una instancia existente a un nuevo dominio después sin empezar de cero.
- Un proveedor SMTP. Esto no es opcional: Mastodon requiere entrega de correo funcional para la confirmación de cuentas, el restablecimiento de contraseñas y los avisos de moderación, y viene sin ninguno configurado por defecto. Cualquier proveedor de correo transaccional funciona.
- 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. Dale tiempo para que se propague antes del Paso 6.
Paso 2: generar todos los secretos por adelantado
Mastodon necesita seis valores secretos distintos antes de arrancar: una clave secreta base de Rails, llaves VAPID para notificaciones push, y tres llaves de cifrado de Active Record. Cada una tiene su propio comando generador, corrido a través de la propia imagen una vez descargada:
mkdir -p /opt/mastodon && cd /opt/mastodon
docker pull ghcr.io/mastodon/mastodon:v4.7.3
docker run --rm ghcr.io/mastodon/mastodon:v4.7.3 bundle exec rails secret
docker run --rm ghcr.io/mastodon/mastodon:v4.7.3 bundle exec rake mastodon:webpush:generate_vapid_key
docker run --rm ghcr.io/mastodon/mastodon:v4.7.3 bundle exec rake db:encryption:init
El primer comando imprime SECRET_KEY_BASE. El segundo imprime un par VAPID_PRIVATE_KEY y VAPID_PUBLIC_KEY. El tercero imprime los tres valores ACTIVE_RECORD_ENCRYPTION_* de una vez. Guarda cada uno de ellos:
cat > .env <<'EOF'
POSTGRES_PASSWORD=generate-with-openssl-rand-hex-24
REDIS_PASSWORD=generate-with-openssl-rand-hex-24
SECRET_KEY_BASE=paste-from-rails-secret
VAPID_PRIVATE_KEY=paste-from-generate-vapid-key
VAPID_PUBLIC_KEY=paste-from-generate-vapid-key
AR_ENCRYPTION_PRIMARY_KEY=paste-from-db-encryption-init
AR_ENCRYPTION_DETERMINISTIC_KEY=paste-from-db-encryption-init
AR_ENCRYPTION_KEY_DERIVATION_SALT=paste-from-db-encryption-init
SMTP_SERVER=smtp.your-provider.com
SMTP_LOGIN=your-smtp-username
SMTP_PASSWORD=your-smtp-password
EOF
chmod 600 .env
Paso 3: escribir el archivo Compose
services:
mastodon:
image: ghcr.io/mastodon/mastodon:v4.7.3
restart: unless-stopped
command: bash -c "bundle exec rails db:prepare && bundle exec puma -C config/puma.rb"
env_file: .env
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
environment:
LOCAL_DOMAIN: social.yourdomain.com
DB_HOST: db
DB_PORT: "5432"
DB_NAME: mastodon
DB_USER: mastodon
DB_PASS: ${POSTGRES_PASSWORD}
REDIS_HOST: redis
REDIS_PORT: "6379"
REDIS_PASSWORD: ${REDIS_PASSWORD}
STREAMING_API_BASE_URL: wss://social.yourdomain.com
SMTP_FROM_ADDRESS: notifications@yourdomain.com
volumes:
- mastodon_system:/mastodon/public/system
expose:
- "3000"
streaming:
image: ghcr.io/mastodon/mastodon-streaming:v4.7.3
restart: unless-stopped
command: node ./streaming/index.js
env_file: .env
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
environment:
PORT: "4000"
LOCAL_DOMAIN: social.yourdomain.com
DB_HOST: db
DB_PORT: "5432"
DB_NAME: mastodon
DB_USER: mastodon
DB_PASS: ${POSTGRES_PASSWORD}
REDIS_HOST: redis
REDIS_PORT: "6379"
REDIS_PASSWORD: ${REDIS_PASSWORD}
expose:
- "4000"
sidekiq:
image: ghcr.io/mastodon/mastodon:v4.7.3
restart: unless-stopped
command: bundle exec sidekiq
env_file: .env
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
environment:
LOCAL_DOMAIN: social.yourdomain.com
DB_HOST: db
DB_PORT: "5432"
DB_NAME: mastodon
DB_USER: mastodon
DB_PASS: ${POSTGRES_PASSWORD}
REDIS_HOST: redis
REDIS_PORT: "6379"
REDIS_PASSWORD: ${REDIS_PASSWORD}
SMTP_FROM_ADDRESS: notifications@yourdomain.com
volumes:
- mastodon_system:/mastodon/public/system
db:
image: postgres:16.4-alpine
restart: unless-stopped
environment:
POSTGRES_USER: mastodon
POSTGRES_DB: mastodon
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- mastodon_db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U mastodon"]
interval: 5s
timeout: 5s
retries: 10
redis:
image: redis:7.2.5-alpine
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD}
env_file: .env
volumes:
- mastodon_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:
- mastodon
- streaming
volumes:
mastodon_system:
mastodon_db_data:
mastodon_redis_data:
caddy_data:
caddy_config:
Esta guía fija v4.7.3 en las tres imágenes de Mastodon, la versión estable vigente al momento de escribir esto. El comando del servicio mastodon corre rails db:prepare antes de que Puma arranque, el mismo patrón que usan las apps Rails en general, así que la configuración del esquema sucede automáticamente en el primer arranque y en cada actualización posterior. streaming es un proceso Node.js genuinamente separado, no un hilo dentro de la app Rails, por eso tiene su propia imagen y su propia STREAMING_API_BASE_URL apuntada desde el servicio principal. mastodon_system es un volumen compartido entre mastodon y sidekiq específicamente, ya que Sidekiq es lo que realmente procesa las subidas de medios en segundo plano; ambos necesitan ver los mismos archivos.
Verifica que el archivo se analiza:
docker compose config
Paso 4: agregar Caddy, incluida la ruta de streaming
social.yourdomain.com {
@streaming path /api/v1/streaming*
reverse_proxy @streaming streaming:4000
reverse_proxy mastodon:3000
}
El cliente web de Mastodon llama a /api/v1/streaming para su línea de tiempo en tiempo real, lo cual tiene que llegar al servicio streaming separado, no al proceso Rails principal; todo lo demás va a mastodon:3000. Valida que el Caddyfile se lea correctamente antes de confiar en él:
docker run --rm -v "$PWD/Caddyfile:/etc/caddy/Caddyfile" caddy:2 caddy validate --config /etc/caddy/Caddyfile
Paso 5: arrancar la pila y crear una cuenta de administrador
docker compose up -d
docker compose ps
curl -I https://social.yourdomain.com
Deberías ver cada servicio en Up (db como healthy) y el curl devolver HTTP/2 200. Crea la primera cuenta desde la línea de comandos, con privilegios de administrador:
docker compose exec mastodon bin/tootctl accounts create admin \
--email admin@yourdomain.com --confirmed --role Owner
Esto imprime una contraseña generada; inicia sesión con ella y cámbiala de inmediato desde la interfaz web.
Paso 6: respaldar Postgres y el volumen de medios
cat > /opt/mastodon/backup.sh <<'EOF'
#!/bin/sh
cd /opt/mastodon
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
docker compose exec -T db pg_dump -U mastodon mastodon | gzip > backups/db-$TIMESTAMP.sql.gz
find backups -mtime +14 -delete
EOF
mkdir -p /opt/mastodon/backups
chmod +x /opt/mastodon/backup.sh
(crontab -l 2>/dev/null; echo "0 3 * * * /opt/mastodon/backup.sh") | crontab -
El volumen mastodon_system guarda cada adjunto multimedia subido y crece sin límite a medida que la gente publica; respáldalo por separado con cualquier herramienta de snapshot de disco completo o de volumen que ofrezca tu proveedor de servidor, un pg_dump solo no lo cubre. Ejecuta el script una vez a mano y verifica que ls -lh backups muestre el archivo de hoy.
Paso 7: actualizar
docker compose pull
docker compose up -d
db:prepare en el comando del servicio mastodon vuelve a correr en cada reinicio, aplicando cualquier migración que haya venido con la nueva versión. Lee las notas de la versión del propio Mastodon antes de un salto de varias versiones; el proyecto ocasionalmente señala una versión intermedia obligatoria para cambios de esquema más grandes.
El atajo: hazlo con Railyard
Todo lo anterior, Postgres y Redis conectados a los tres procesos basados en Rails, el servicio de streaming separado con su propio enrutamiento, Sidekiq compartiendo el volumen de medios, es también lo que configura por ti la plantilla de pila Mastodon de Railyard. Conecta un servidor, y luego elige Mastodon en el catálogo de plantillas: app.railyard.run/deploy?stack=mastodon-stack. Railyard genera cada secreto del Paso 2 por ti y conecta automáticamente las URL de la base de datos, Redis y streaming; tú sigues agregando tu propio proveedor SMTP después, igual que en el Paso 2 de arriba, Railyard tampoco configura uno por ti, y los medios subidos siguen yendo por defecto a un volumen local compartido a menos que tú mismo los dirijas a almacenamiento de objetos.
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
La línea de tiempo en tiempo real nunca se actualiza, pero publicar y navegar funcionan bien. El Caddyfile no está enrutando /api/v1/streaming al servicio streaming; revisa la coincidencia de ruta del Paso 4 contra lo que realmente solicita la pestaña de red de tu navegador.
Nunca llega un correo de confirmación para un registro nuevo. El SMTP no está configurado o las credenciales son incorrectas; revisa docker compose logs mastodon en busca de un error de SMTP en el momento en que se intentó el registro. No hay forma de evitar este requisito, Mastodon no confirma una cuenta sin él.
Sidekiq se atrasa, las publicaciones de otras instancias llegan tarde. Revisa docker compose logs sidekiq en busca de fallos repetidos de tareas o un reinicio por falta de memoria; en un CX33, esto suele ser contención de recursos con Postgres y el proceso web durante un pico de tráfico, que es el punto donde separar Postgres en su propio servidor empieza a valer la pena.
El HTTPS no se emite. Que docker compose logs caddy muestre errores de ACME repetidos casi siempre significa que el registro DNS del Paso 1 no se ha propagado o apunta a la IP equivocada.
El disco se llena por el volumen de medios. Mastodon guarda en caché localmente los medios remotos (imágenes y video de otras instancias) por defecto; tootctl media remove --days 30 desde dentro del contenedor mastodon limpia los medios remotos en caché más antiguos que una ventana dada sin tocar las publicaciones de tus propios usuarios.
Preguntas frecuentes
¿Puedo cambiar el dominio de mi instancia después? No, los identificadores de cuenta están atados al dominio de forma permanente; esto es una limitación de todo el fediverso, no algo que la propia configuración de Mastodon pueda evitar, decide el dominio real antes de invitar a nadie.
¿De verdad necesito 8 GB de RAM para una instancia pequeña con un puñado de usuarios? El piso es más bajo, pero una vez que Postgres, Redis, el servidor de streaming y Sidekiq corren todos en una sola máquina, 8 GB es donde las cosas dejan de ir justas; un CX23 con 4 GB técnicamente arranca esta pila pero deja muy poco margen para picos de tráfico o tormentas de federación.
¿Cuál es la diferencia entre esto y unirse a un servidor Mastodon existente? Correr el tuyo propio te da tu propia política de moderación, tu propio dominio, y control total sobre la retención de datos; unirte a una instancia existente cambia ese control por cero trabajo operativo.
¿El almacenamiento de objetos puede reemplazar el volumen de medios local? Sí, Mastodon admite almacenamiento compatible con S3 para medios a través de su propia configuración; esta guía lo mantiene local para una primera configuración directa, vale la pena revisarlo una vez que el volumen de medios se vuelva el principal consumidor de disco.
¿La federación viene desactivada por defecto en algún momento? No, una instancia nueva federa con la red más amplia en cuanto es alcanzable por HTTPS; existe un modo de lista de admitidos separado si quieres una instancia cerrada en su lugar, no cubierto aquí.
Una instancia de Mastodon corriendo correctamente son cinco servicios de acuerdo en un dominio y un conjunto de credenciales, no una sola cosa complicada. Una vez que el DNS, la ruta de streaming y el SMTP están bien, el trabajo continuo son los respaldos, la actualización ocasional de versión, y vigilar a Sidekiq bajo tráfico real.
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.