Cómo instalar la red social Mastodon en Debian 12

Mastodon es una red social libre, descentralizada y de código abierto. Se creó como alternativa a Twitter. Al igual que en Twitter, la gente puede seguirse entre sí y publicar mensajes, imágenes y vídeos. Pero a diferencia de Twitter, no existe un almacén central ni una autoridad para el contenido.

En su lugar, Mastodon opera a través de miles de servidores diferentes, cada uno gestionado por varios miembros de la comunidad. Los usuarios registrados en un servidor pueden conectarse fácilmente con los usuarios de la otra red y seguirse entre instancias.

Cualquiera puede instalar su instancia de un servidor Mastodon. Este tutorial te enseñará a configurar tu instancia de Mastodon en un servidor con Debian 12 utilizando Docker. Docker facilita la instalación de Mastodon al contener todos los paquetes y servicios necesarios en contenedores.

Requisitos previos

  • Un servidor con Debian 12 con un mínimo de 2 núcleos de CPU y 2 GB de memoria. Tendrás que actualizar el servidor según los requisitos.
  • Un usuario no root con privilegios sudo.
  • Un nombre de dominio completo (FQDN) que apunte a tu servidor. Para nuestros propósitos, utilizaremos mastodon.example.com como nombre de dominio.
  • Mastodon envía notificaciones por correo electrónico a los usuarios. Te recomendamos que utilices un servicio de correo transaccional de terceros como Mailgun, SendGrid, Amazon SES o Sparkpost. Las instrucciones de la guía utilizarán Amazon SES.
  • Asegúrate de que todo está actualizado.
    $ sudo apt update
    
  • Instala los paquetes de utilidades básicas. Puede que algunos ya estén instalados.
    $ sudo apt install curl wget nano software-properties-common dirmngr apt-transport-https ca-certificates lsb-release debian-archive-keyring gnupg2 ufw unzip -y
    

Paso 1 – Configurar el cortafuegos

El primer paso es configurar el cortafuegos. Debian viene con ufw (Uncomplicated Firewall) por defecto.

Comprueba si el cortafuegos está funcionando.

$ sudo ufw status

Deberías obtener la siguiente salida.

Status: inactive

Permite el puerto SSH para que el cortafuegos no rompa la conexión actual al activarlo.

$ sudo ufw allow OpenSSH

Permite también los puertos HTTP y HTTPS.

$ sudo ufw allow http
$ sudo ufw allow https

Habilita el cortafuegos

$ sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup

Comprueba de nuevo el estado del cortafuegos.

$ sudo ufw status

Deberías ver un resultado similar.

Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
80/tcp                     ALLOW       Anywhere
443                        ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
80/tcp (v6)                ALLOW       Anywhere (v6)
443 (v6)                   ALLOW       Anywhere (v6)

Paso 2 – Instala Docker y Docker Compose

Debian 12 incluye una versión antigua de Docker. Para instalar la última versión, importa primero la clave GPG de Docker.

$ sudo install -m 0755 -d /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
$ sudo chmod a+r /etc/apt/keyrings/docker.gpg

Crea el archivo del repositorio de Docker.

$ echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Actualiza la lista de repositorios del sistema.

$ sudo apt update

Instala la última versión de Docker.

$ sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Comprueba que se está ejecutando.

$ sudo systemctl status docker
? docker.service - Docker Application Container Engine
     Loaded: loaded (/lib/systemd/system/docker.service; enabled; preset: enabled)
     Active: active (running) since Mon 2024-01-01 09:00:14 UTC; 17s ago
TriggeredBy: ? docker.socket
       Docs: https://docs.docker.com
   Main PID: 1839 (dockerd)
      Tasks: 9
     Memory: 27.6M
        CPU: 598ms
     CGroup: /system.slice/docker.service
             ??1839 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

Por defecto, Docker requiere privilegios de root. Si quieres evitar utilizar sudo cada vez que ejecutes el comando docker, añade tu nombre de usuario al grupo docker.

$ sudo usermod -aG docker $(whoami)

Tienes que salir del servidor y volver a entrar como el mismo usuario para activar este cambio o utilizar el siguiente comando.

$ su - ${USER}

Confirma que tu usuario se ha añadido al grupo Docker.

$ groups
navjot sudo users docker

Paso 3 – Preparación para la instalación

El límite por defecto de los recuentos mmap es muy bajo para Elasticsearch. Ejecuta el siguiente comando para comprobar el valor por defecto.

$ sudo sysctl vm.max_map_count

Obtendrás la siguiente salida.

vm.max_map_count = 65530

Aumenta el valor utilizando los siguientes comandos.

$ echo "vm.max_map_count=262144" | sudo tee /etc/sysctl.d/90-max_map_count.conf
vm.max_map_count=262144
$ sudo sysctl --load /etc/sysctl.d/90-max_map_count.conf
vm.max_map_count=262144

Paso 4 – Instalar Mastodon

Crea directorios y establece la propiedad

Crea directorios para Mastodon y los servicios relacionados.

$ sudo mkdir -p /opt/mastodon/database/{postgresql,pgbackups,redis,elasticsearch}
$ sudo mkdir -p /opt/mastodon/web/{public,system}
$ sudo mkdir -p /opt/mastodon/branding

Establece las propiedades adecuadas para los directorios Elasticsearch, web y de copia de seguridad.

$ sudo chown 991:991 /opt/mastodon/web/{public,system}
$ sudo chown 1000 /opt/mastodon/database/elasticsearch
$ sudo chown 70:70 /opt/mastodon/database/pgbackups

Cambia al directorio Mastodon.

$ cd /opt/mastodon

Crea archivos de entorno y docker compose

Crea archivos de entorno para la aplicación y la base de datos.

$ sudo touch application.env database.env

Crea y abre el archivo Docker compose para editarlo.

$ sudo nano docker-compose.yml

Pega en él el siguiente código.

services:
  postgresql:
    image: postgres:16-alpine
    env_file: database.env
    restart: always
    shm_size: 512mb
    healthcheck:
      test: ['CMD', 'pg_isready', '-U', 'postgres']
    volumes:
      - postgresql:/var/lib/postgresql/data
      - pgbackups:/backups
    networks:
      - internal_network

  redis:
    image: redis:7-alpine
    restart: always
    healthcheck:
      test: ['CMD', 'redis-cli', 'ping']
    volumes:
      - redis:/data
    networks:
      - internal_network

  redis-volatile:
    image: redis:7-alpine
    restart: always
    healthcheck:
      test: ['CMD', 'redis-cli', 'ping']
    networks:
      - internal_network

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.17.16
    restart: always
    env_file: database.env
    environment:
      - cluster.name=elasticsearch-mastodon
      - discovery.type=single-node
      - bootstrap.memory_lock=true
      - xpack.security.enabled=true
      - ingest.geoip.downloader.enabled=false
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m -Des.enforce.bootstrap.checks=true"
      - xpack.license.self_generated.type=basic
      - xpack.watcher.enabled=false
      - xpack.graph.enabled=false
      - xpack.ml.enabled=false
      - thread_pool.write.queue_size=1000
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    healthcheck:
      test: ["CMD-SHELL", "nc -z elasticsearch 9200"]
    volumes:
      - elasticsearch:/usr/share/elasticsearch/data
    networks:
      - internal_network
    ports:
      - '127.0.0.1:9200:9200'

  website:
    image: tootsuite/mastodon:v4.2.3
    env_file:
      - application.env
      - database.env
    command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"
    restart: always
    depends_on:
      - postgresql
      - redis
      - redis-volatile
      - elasticsearch
    ports:
      - '127.0.0.1:3000:3000'
    networks:
      - internal_network
      - external_network
    healthcheck:
      test: ['CMD-SHELL', 'wget -q --spider --proxy=off localhost:3000/health || exit 1']
    volumes:
      - uploads:/mastodon/public/system

  shell:
    image: tootsuite/mastodon:v4.2.3
    env_file:
      - application.env
      - database.env
    command: /bin/bash
    restart: "no"
    networks:
      - internal_network
      - external_network
    volumes:
      - uploads:/mastodon/public/system
      - static:/static

  streaming:
    image: tootsuite/mastodon:v4.2.3
    env_file:
      - application.env
      - database.env
    command: node ./streaming
    restart: always
    depends_on:
      - postgresql
      - redis
      - redis-volatile
      - elasticsearch
    ports:
      - '127.0.0.1:4000:4000'
    networks:
      - internal_network
      - external_network
    healthcheck:
      test: ['CMD-SHELL', 'wget -q --spider --proxy=off localhost:4000/api/v1/streaming/health || exit 1']

  sidekiq:
    image: tootsuite/mastodon:v4.2.3
    env_file:
      - application.env
      - database.env
    command: bundle exec sidekiq
    restart: always
    depends_on:
      - postgresql
      - redis
      - redis-volatile
      - website
    networks:
      - internal_network
      - external_network
    healthcheck:
      test: ['CMD-SHELL', "ps aux | grep '[s]idekiq\ 6' || false"]
    volumes:
      - uploads:/mastodon/public/system

networks:
  external_network:
  internal_network:
    internal: true

volumes:
  postgresql:
    driver_opts:
      type: none
      device: /opt/mastodon/database/postgresql
      o: bind
  pgbackups:
    driver_opts:
      type: none
      device: /opt/mastodon/database/pgbackups
      o: bind
  redis:
    driver_opts:
      type: none
      device: /opt/mastodon/database/redis
      o: bind
  elasticsearch:
    driver_opts:
      type: none
      device: /opt/mastodon/database/elasticsearch
      o: bind
  uploads:
    driver_opts:
      type: none
      device: /opt/mastodon/web/system
      o: bind
  static:
    driver_opts:
      type: none
      device: /opt/mastodon/web/public
      o: bind

Guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te pida.

En el momento de escribir el tutorial, la última versión disponible de Mastodon es la v4.2.3. Consulta la página de versiones de Mastodon en GitHub y ajusta la versión en el archivo de composición de Docker según corresponda. También estamos utilizando las últimas versiones de PostgreSQL y Redis. Puedes ajustarlas según tus necesidades. En este momento estamos utilizando Elasticsearch 7.17.16.

Crear Secretos de Aplicación

El siguiente paso es crear los valores secretos de la aplicación.

Genera los valores SECRET_KEY_BASE y OTP_SECRET ejecutando el siguiente comando dos veces. La primera vez tardará un poco, ya que extraerá las imágenes.

$ docker compose run --rm shell bundle exec rake secret
349623c049e3b856f6848638146e459857862b908ed387bbef372a30d9bd7c604fc4de5338addc86bd369a99d38ef59bacfa28e02a1750f7094ea6ede05457b8

También puedes utilizar la utilidad openssl para lo mismo.

$ openssl rand -hex 64
ae01cf7d4dfae0182461a1345f1f2bf159658a27339ffafe7d356bef9ee8d4fa015ab2e72a608f236bd8e3f9b2af2dcb1d55ee5c8e43646959112c7da5582f4b

Genera los valores VAPID_PRIVATE_KEY y VAPID_PUBLIC_KEY utilizando el siguiente comando.

$ docker compose run --rm shell bundle exec rake mastodon:webpush:generate_vapid_key

Obtendrás un resultado similar.

VAPID_PRIVATE_KEY=u2qsCs5JdmdmMLnUuU0sgmFGvZedteJz-lFB_xF4_ac=
VAPID_PUBLIC_KEY=BJXjE2hIXvFpo6dnHqyf1i-2PcP-cBoL95UCmhhxwlAgtFw_vnrYp4GBneR7_cmI9LZUYjHFh-TBAPSb9WTqH9A=

Utiliza la utilidad openssl para generar las contraseñas de PostgreSQL y Elasticsearch.

$ openssl rand -hex 15
dd0bd7a95960623ed8e084a1fb7d5c
$ openssl rand -hex 15
0fb52834c991b5e296c647166185bc

Archivos de entorno Mastodon

Abre el archivo application.env para editarlo.

$ sudo nano application.env

Pega en él las siguientes líneas.

# environment
RAILS_ENV=production
NODE_ENV=production

# domain
LOCAL_DOMAIN=mastodon.example.com

# redirect to the first profile
SINGLE_USER_MODE=false

# do not serve static files
RAILS_SERVE_STATIC_FILES=false

# concurrency
WEB_CONCURRENCY=2
MAX_THREADS=5

# pgbouncer
#PREPARED_STATEMENTS=false

# locale
DEFAULT_LOCALE=en

# email, not used
SMTP_SERVER=email-smtp.us-west-2.amazonaws.com
SMTP_PORT=587
SMTP_LOGIN=AKIA3FIG4NVFB343PZEI
SMTP_PASSWORD=AZX01WiA6JGbeZ2pwVXnyC9DhEa2nKcmXSu/zbLp
[email protected]

# secrets
SECRET_KEY_BASE=349623c049e3b856f6848638146e459857862b908ed387bbef372a30d9bd7c604fc4de5338addc86bd369a99d38ef59bacfa28e02a1750f7094ea6ede05457b8
OTP_SECRET=ae01cf7d4dfae0182461a1345f1f2bf159658a27339ffafe7d356bef9ee8d4fa015ab2e72a608f236bd8e3f9b2af2dcb1d55ee5c8e43646959112c7da5582f4b

# Changing VAPID keys will break push notifications
VAPID_PRIVATE_KEY=oNe_4BEL7Tpc3iV8eMtLegfLwrzA7ifitGJ2YOg3dUM=
VAPID_PUBLIC_KEY=BKBgmB90vIrJg6Ifq3cCHixalyPghJDkui9vm1wscxvAfNNoAQL0KinoxRTLDp0UFlGK_ahUG2n4W2n4x9AUAWM=

# IP and session retention
# -----------------------
# Make sure to modify the scheduling of ip_cleanup_scheduler in config/sidekiq.yml
# to be less than daily if you lower IP_RETENTION_PERIOD below two days (172800).
# -----------------------
IP_RETENTION_PERIOD=2592000
SESSION_RETENTION_PERIOD=2592000

Guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te solicite.

Hemos habilitado el servicio de correo de Amazon SES. Si no lo necesitas, puedes eliminar la sección. Por defecto, Mastodon retiene una dirección IP durante 1 año, pero lo hemos cambiado a 30 días (2592000 segundos). Puedes cambiarlo según tus necesidades. Asegúrate de mantenerla durante más de 2 días, de lo contrario, tendrás que hacer algunos ajustes más que quedan fuera del alcance de nuestro tutorial.

Abre el archivo database.env para editarlo.

$ sudo nano database.env

Pega en él las siguientes líneas.

# postgresql configuration
POSTGRES_USER=mastodon
POSTGRES_DB=mastodon
POSTGRES_PASSWORD=0fb52834c991b5e296c647166185bc
PGPASSWORD=0fb52834c991b5e296c647166185bc
PGPORT=5432
PGHOST=postgresql
PGUSER=mastodon

# pgbouncer configuration
#POOL_MODE=transaction
#ADMIN_USERS=postgres,mastodon
#DATABASE_URL="postgres://mastodon:0fb52834c991b5e296c647166185bc@postgresql:5432/mastodon"

# elasticsearch
ELASTIC_PASSWORD=dd0bd7a95960623ed8e084a1fb7d5c

# mastodon database configuration
#DB_HOST=pgbouncer
DB_HOST=postgresql
DB_USER=mastodon
DB_NAME=mastodon
DB_PASS=0fb52834c991b5e296c647166185bc
DB_PORT=5432

REDIS_HOST=redis
REDIS_PORT=6379

CACHE_REDIS_HOST=redis-volatile
CACHE_REDIS_PORT=6379

ES_ENABLED=true
ES_HOST=elasticsearch
ES_PORT=9200
ES_USER=elastic
ES_PASS=dd0bd7a95960623ed8e084a1fb7d5c

Guarda el archivo pulsando Ctrl + X e introduciendo Y cuando te lo pida.

Prepara Mastodon

Prepara los archivos estáticos para que los sirva Nginx. Este paso va a llevar algo de tiempo porque Docker sacará todas las imágenes por primera vez.

$ docker compose run --rm shell bash -c "cp -r /opt/mastodon/public/* /static/"

Activa la capa de datos.

$ docker compose up -d postgresql redis redis-volatile

Comprueba el estado de los contenedores.

$ watch docker compose ps

Reloj Docker Compose

Espera a running (healthy), luego pulsa Ctrl + C e inicializa la base de datos utilizando el siguiente comando.

$ docker compose run --rm shell bundle exec rake db:setup

Si aparece el error de que la base de datos mastodon ya existe, ejecuta el siguiente comando.

$ docker compose run --rm shell bundle exec rake db:migrate

Paso 5 – Instalar Nginx

Debian 12 incluye una versión antigua de Nginx. Para instalar la última versión, necesitas descargar el repositorio oficial de Nginx.

Importa la clave de firma de Nginx.

$ curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
    | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null

Añade el repositorio de la versión principal de Nginx.

$ echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/mainline/debian `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list

Actualiza los repositorios del sistema.

$ sudo apt update

Instala Nginx.

$ sudo apt install nginx

Verifica la instalación. En los sistemas Debian, necesitas sudo para ejecutar el siguiente comando.

$ sudo nginx -v
nginx version: nginx/1.25.3

Inicia el servidor Nginx.

$ sudo systemctl start nginx

Comprueba el estado del servidor.

$ sudo systemctl status nginx
? nginx.service - nginx - high performance web server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running) since Mon 2024-01-01 10:17:38 UTC; 4s ago
       Docs: https://nginx.org/en/docs/
    Process: 8972 ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf (code=exited, status=0/SUCCESS)
   Main PID: 8973 (nginx)
      Tasks: 3 (limit: 4637)
     Memory: 2.9M
        CPU: 17ms
     CGroup: /system.slice/nginx.service
             ??8973 "nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf"
             ??8974 "nginx: worker process"
             ??8975 "nginx: worker process"

Jan 01 10:17:38 mastodon systemd[1]: Starting nginx.service - nginx - high performance web server...
Jan 01 10:17:38 mastodon systemd[1]: Started nginx.service - nginx - high performance web server.

Paso 6 – Instalar SSL

Necesitamos instalar Certbot para generar el certificado SSL. Puedes instalar Certbot utilizando el repositorio de Debian u obtener la última versión utilizando la herramienta Snapd. Nosotros utilizaremos la versión Snapd.

Debian 12 no viene con Snapd instalado. Instala el paquete Snapd.

$ sudo apt install snapd

Ejecuta los siguientes comandos para asegurarte de que tu versión de Snapd está actualizada. Asegúrate de que tu versión de Snapd está actualizada.

$ sudo snap install core
$ sudo snap refresh core

Instala Certbot.

$ sudo snap install --classic certbot

Utiliza el siguiente comando para asegurarte de que se ejecuta el comando Certbot creando un enlace simbólico al directorio /usr/bin.

$ sudo ln -s /snap/bin/certbot /usr/bin/certbot

Verifica la instalación.

$ certbot --version
certbot 2.8.0

Ejecuta el siguiente comando para generar un certificado SSL.

$ sudo certbot certonly --nginx --agree-tos --no-eff-email --staple-ocsp --preferred-challenges http -m [email protected] -d mastodon.example.com

El comando anterior descargará un certificado en el directorio /etc/letsencrypt/live/mastodon.example.com de tu servidor.

Genera un certificado de grupo Diffie-Hellman.

$ sudo openssl dhparam -dsaparam -out /etc/ssl/certs/dhparam.pem 4096

Comprueba el servicio programador de renovaciones de Certbot.

$ systemctl list-timers

Encontrarás snap.certbot.renew.service como uno de los servicios programados para ejecutarse.

NEXT                        LEFT        LAST                        PASSED             UNIT                    ACTIVATES
-----------------------------------------------------------------------------------------------------------------------------------------
Mon 2024-01-01 20:03:52 UTC 9h left     Mon 2023-12-11 21:56:24 UTC 2 weeks 6 days ago apt-daily.timer         apt-daily.service
Mon 2024-01-01 21:06:00 UTC 10h left    -                           -                  snap.certbot.renew.timersnap.certbot.renew.service
Tue 2024-01-02 00:00:00 UTC 13h left    -                           -                  dpkg-db-backup.timer    dpkg-db-backup.service

Realiza una ejecución en seco del proceso para comprobar si la renovación SSL funciona correctamente.

$ sudo certbot renew --dry-run

Si no ves ningún error, ya está todo listo. Tu certificado se renovará automáticamente.

Paso 7 – Configurar Nginx

Abre el archivo /etc/nginx/nginx.conf para editarlo.

$ sudo nano /etc/nginx/nginx.conf

Añade la siguiente línea antes de la línea include /etc/nginx/conf.d/*.conf;.

server_names_hash_bucket_size 64;

Guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te solicite.

Crea y abre el archivo /etc/nginx/conf.d/mastodon.conf para editarlo.

$ sudo nano /etc/nginx/conf.d/mastodon.conf

Pega en él el siguiente código.

map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

upstream backend {
    server 127.0.0.1:3000 fail_timeout=0;
}

upstream streaming {
    server 127.0.0.1:4000 fail_timeout=0;
}

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=1g;

server {
  listen 80 default_server;
  server_name mastodon.example.com;
  location / { return 301 https://$host$request_uri; }
}

server {
   listen 443 ssl;
   server_name mastodon.example.com;
   
   access_log  /var/log/nginx/mastodon.access.log;
   error_log   /var/log/nginx/mastodon.error.log;

   http2 on; # Enable HTTP/2 - works only on Nginx 1.25.1+

   ssl_certificate /etc/letsencrypt/live/mastodon.example.com/fullchain.pem;
   ssl_certificate_key /etc/letsencrypt/live/mastodon.example.com/privkey.pem;
   ssl_trusted_certificate /etc/letsencrypt/live/mastodon.example.com/chain.pem;
   ssl_session_timeout 1d;

   # Enable TLS versions (TLSv1.3 is required upcoming HTTP/3 QUIC).
   ssl_protocols TLSv1.2 TLSv1.3;

   # Enable TLSv1.3's 0-RTT. Use $ssl_early_data when reverse proxying to
   # prevent replay attacks.
   #
   # @see: https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_early_data
   ssl_early_data on;

   ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384';
   ssl_prefer_server_ciphers on;
   ssl_session_cache shared:SSL:10m;
   ssl_session_tickets off;
   
   keepalive_timeout    70;
   sendfile             on;
   client_max_body_size 80m;

   # OCSP Stapling ---
   # fetch OCSP records from URL in ssl_certificate and cache them
   ssl_stapling on;
   ssl_stapling_verify on;
   ssl_dhparam /etc/ssl/certs/dhparam.pem;

   add_header X-Early-Data $tls1_3_early_data;
   
   root /opt/mastodon/web/public;
   
   gzip on;
   gzip_disable "msie6";
   gzip_vary on;
   gzip_proxied any;
   gzip_comp_level 6;
   gzip_buffers 16 8k;
   gzip_http_version 1.1;
   gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml image/x-icon;
   
   add_header Strict-Transport-Security "max-age=31536000" always;

  location / {
    try_files $uri @proxy;
  }

  location ~ ^/(system/accounts/avatars|system/media_attachments/files) {
    add_header Cache-Control "public, max-age=31536000, immutable";
    add_header Strict-Transport-Security "max-age=31536000" always;
    root /opt/mastodon/;
    try_files $uri @proxy;
  }

  location ~ ^/(emoji|packs) {
    add_header Cache-Control "public, max-age=31536000, immutable";
    add_header Strict-Transport-Security "max-age=31536000" always;
    try_files $uri @proxy;
  }

  location /sw.js {
    add_header Cache-Control "public, max-age=0";
    add_header Strict-Transport-Security "max-age=31536000" always;
    try_files $uri @proxy;
  }

  location @proxy {
    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 Proxy "";
    proxy_pass_header Server;

    proxy_pass http://backend;
    proxy_buffering on;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    proxy_cache CACHE;
    proxy_cache_valid 200 7d;
    proxy_cache_valid 410 24h;
    proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
    add_header X-Cached $upstream_cache_status;
    add_header Strict-Transport-Security "max-age=31536000" always;

    tcp_nodelay on;
  }

  location /api/v1/streaming {
    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 Proxy "";

    proxy_pass http://streaming;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  error_page 500 501 502 503 504 /500.html;
}

# This block is useful for debugging TLS v1.3. Please feel free to remove this
# and use the `$ssl_early_data` variable exposed by NGINX directly should you
# wish to do so.
map $ssl_early_data $tls1_3_early_data {
  "~." $ssl_early_data;
  default "";
}

Cuando hayas terminado, guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te solicite.

Verifica la sintaxis del archivo de configuración de Nginx.

$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Reinicia el servidor Nginx.

$ sudo systemctl restart nginx

Paso 8 – Iniciar Mastodon

Herramienta Tootctl CLI

La herramienta Tootctl CLI se utiliza para realizar tareas administrativas en Mastodon. Necesitamos hacerla accesible en el shell del host.

Crea el archivo /usr/local/bin/tootctl y ábrelo para editarlo.

$ sudo nano /usr/local/bin/tootctl

Pega en él el siguiente código.

#!/bin/bash
docker compose -f /opt/mastodon/docker-compose.yml run --rm shell tootctl "$@"

Guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te pida.

Da permiso de ejecución al archivo.

$ sudo chmod +x /usr/local/bin/tootctl

Fichero de servicio Mastodon

Puedes iniciar los contenedores Mastodon utilizando el comando Docker compose, pero es más fácil hacerlo mediante un archivo de unidad systemd.

Crea y abre el archivo de servicio Mastodon para editarlo.

$ sudo nano /etc/systemd/system/mastodon.service

Pega en él el siguiente código.

[Unit]
Description=Mastodon service
After=docker.service

[Service]
Type=oneshot
RemainAfterExit=yes

WorkingDirectory=/opt/mastodon
ExecStart=/usr/bin/docker compose -f /opt/mastodon/docker-compose.yml up -d
ExecStop=/usr/bin/docker compose -f /opt/mastodon/docker-compose.yml down

[Install]
WantedBy=multi-user.target

Guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te solicite.

Recarga el demonio del sistema para iniciar el archivo de servicio.

$ sudo systemctl daemon-reload

Habilita e inicia el servicio Mastodon.

$ sudo systemctl enable --now mastodon.service

Comprueba el estado de los contenedores Docker.

$ watch docker compose -f /opt/mastodon/docker-compose.yml ps

Estado del contenedor Docker Mastodon

Una vez que el estado de los contenedores cambie a running (healthy), sal de la pantalla pulsando Ctrl + C.

Crea el usuario admin para Mastodon y anota la contraseña proporcionada.

$ tootctl accounts create navjot --email [email protected] --confirmed --role Owner
OK
New password: 1338afbe1b4e06e823b6625da80cb537

Si quieres cerrar los registros de usuarios, utiliza el siguiente comando.

$ tootctl settings registrations close

Para abrir de nuevo los registros, emite el siguiente comando.

$ tootctl settings registrations open

Necesitarás hacer un toot antes de poder crear y poblar los índices de Elasticsearch. Una vez que hayas hecho un toot, emite el siguiente comando.

$ tootctl search deploy

Es posible que obtengas el siguiente error.

/opt/mastodon/vendor/bundle/ruby/3.0.0/gems/ruby-progressbar-1.11.0/lib/ruby-progressbar/progress.rb:76:in `total=': You can't set the item's total value to less than the current progress. (ProgressBar::InvalidProgressError)
        from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/ruby-progressbar-1.11.0/lib/ruby-progressbar/base.rb:178:in `block in update_progress'
        from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/ruby-progressbar-1.11.0/lib/ruby-progressbar/output.rb:43:in `with_refresh'
        from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/ruby-progressbar-1.11.0/lib/ruby-progressbar/base.rb:177:in `update_progress'
        from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/ruby-progressbar-1.11.0/lib/ruby-progressbar/base.rb:101:in `total='
        from /opt/mastodon/lib/mastodon/search_cli.rb:67:in `deploy'
        from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/thor-1.2.1/lib/thor/command.rb:27:in `run'
        from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/thor-1.2.1/lib/thor/invocation.rb:127:in `invoke_command'
        from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/thor-1.2.1/lib/thor.rb:392:in `dispatch'
        from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/thor-1.2.1/lib/thor/invocation.rb:116:in `invoke'
        from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/thor-1.2.1/lib/thor.rb:243:in `block in subcommand'
        from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/thor-1.2.1/lib/thor/command.rb:27:in `run'
        from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/thor-1.2.1/lib/thor/invocation.rb:127:in `invoke_command'
        from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/thor-1.2.1/lib/thor.rb:392:in `dispatch'
        from /opt/mastodon/vendor/bundle/ruby/3.0.0/gems/thor-1.2.1/lib/thor/base.rb:485:in `start'
        from /opt/mastodon/bin/tootctl:8:in `<main>'

En este caso, entra en el shell del contenedor del sitio web.

$ docker exec -it mastodon-website-1 /bin/bash

Ejecuta el siguiente comando.

$ sed -E '/progress.total = /d' -i lib/mastodon/search_cli.rb

Sal del intérprete de comandos del contenedor.

$ exit

Ejecuta de nuevo el comando de despliegue de Elasticsearch. A veces el comando puede funcionar más tarde. Se trata de un problema en curso en Mastodon, por lo que no existe una solución definitiva por el momento.

$ tootctl search deploy
Done! 1/?? |-=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=---=|  ETA: ??:??:?? (0 docs/s)
Indexed 1 records, de-indexed 0

Servicios de ayuda adicionales

Vamos a crear otro servicio para eliminar los archivos multimedia descargados.

Crea y abre el servicio de eliminación de archivos multimedia de Mastodon para editarlo.

$ sudo nano /etc/systemd/system/mastodon-media-remove.service

Pega en él el siguiente código.

[Unit]
Description=Mastodon - media remove service
Wants=mastodon-media-remove.timer

[Service]
Type=oneshot
StandardError=null
StandardOutput=null

WorkingDirectory=/opt/mastodon
ExecStart=/usr/bin/docker compose -f /opt/mastodon/docker-compose.yml run --rm shell tootctl media remove

[Install]
WantedBy=multi-user.target

Guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te pida.

Si quieres programar la eliminación de archivos multimedia, puedes configurar un servicio temporizador para ello.

$ sudo nano /etc/systemd/system/mastodon-media-remove.timer

Pega el siguiente código.

[Unit]
Description=Schedule a media remove every week

[Timer]
Persistent=true
OnCalendar=Sat *-*-* 00:00:00
Unit=mastodon-media-remove.service

[Install]
WantedBy=timers.target

Guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te solicite.

Puedes configurar otro servicio para eliminar las tarjetas de previsualización enriquecidas generadas mediante etiquetas OpenGraph.

$ sudo nano /etc/systemd/system/mastodon-preview_cards-remove.service

Pega el siguiente código.

[Unit]
Description=Mastodon - preview cards remove service
Wants=mastodon-preview_cards-remove.timer

[Service]
Type=oneshot
StandardError=null
StandardOutput=null

WorkingDirectory=/opt/mastodon
ExecStart=/usr/bin/docker compose -f /opt/mastodon/docker-compose.yml run --rm shell tootctl preview_cards remove

[Install]
WantedBy=multi-user.target

Guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te solicite.

Configura el servicio de temporizador correspondiente.

$ sudo nano /etc/systemd/system/mastodon-preview_cards-remove.timer

Pega el siguiente código.

[Unit]
Description=Schedule a preview cards remove every week

[Timer]
Persistent=true
OnCalendar=Sat *-*-* 00:00:00
Unit=mastodon-preview_cards-remove.service

[Install]
WantedBy=timers.target

Guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te solicite.

Recarga el demonio del sistema.

$ sudo systemctl daemon-reload

Habilita e inicia los temporizadores.

$ sudo systemctl enable --now mastodon-preview_cards-remove.timer
$ sudo systemctl enable --now mastodon-media-remove.timer

Lista todos los temporizadores para comprobar la programación de los servicios Mastodon.

$ systemctl list-timers
.....
Sat 2024-01-06 00:00:00 UTC 4 days left -                           -                  mastodon-media-remove.timer         mastodon-media-remove.service
Sat 2024-01-06 00:00:00 UTC 4 days left -                           -                  mastodon-preview_cards-remove.timer mastodon-preview_cards-remove.service

Acceder a Mastodon

Visita la URL https://mastodon.example.com para acceder a tu instancia y verás una página similar.

Página de Mastodon

En la captura de pantalla anterior, puedes ver que hay 0 usuarios. Esto se debe a que aún no hemos iniciado sesión. Aunque crees una cuenta de administrador, no aparece en la página principal en la primera ejecución. Para ello, inicia sesión en tu instancia y accederás a la siguiente página.

Mastodon TimeLine Página

Haz clic en la opción Preferencias de la barra lateral derecha para acceder a la configuración. Desde ahí, haz clic en la opción Administración del menú de la izquierda para acceder al panel de administración de Mastodon.

Panel de administración de Mastodon

Haz clic en la opción Configuración del servidor de la barra lateral izquierda.

Mastodon Admin Información de usuario

Aquí, introduce tu nombre de usuario de contacto y tu correo electrónico profesional, que se reflejarán en la página de inicio de tu servidor. Rellena también otra información como la descripción del servidor, el logotipo y las reglas del servidor para personalizar tu instancia de Mastodon.

Paso 9 – Mantenimiento de Mastodon

Para ver el rendimiento y los registros de tu instancia de Mastodon, dirígete a https://mastodon.example.com/sidekiq/.

Aquí puedes ver una lista de varios procesos y tareas programadas relacionadas con tu instancia de Mastodon. También puedes comprobar si hay tareas fallidas en la sección Muertos o Reintentos. También te indicará el uso de memoria de tu instancia.

Mastodon Sidekiq Página

Puedes comprobar el estado de la base de datos de tu instancia desde https://mastodon.example.com/pghero/.

Mastodon PgHero Página

Puedes realizar el mantenimiento de tu base de datos, ejecutar consultas SQL y eliminar índices no utilizados. Para activar las estadísticas de consulta, haz clic en el botón Activar de la página anterior y obtendrás la siguiente información.

Mastodonte Información sobre estadísticas de consulta

Cambia al usuario root.

$ sudo -i su

Cambia al directorio /opt/mastodon/database/postgresql.

$ cd /opt/mastodon/database/postgresql

Abre el archivo postgresql.conf.

$ nano postgresql.conf

Busca la línea #shared_preload_libraries = '' # (change requires restart) y sustitúyela por lo siguiente.

shared_preload_libraries = 'pg_stat_statements'

Añade la siguiente línea al final del archivo.

pg_stat_statements.track = all

Guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te solicite.

Reinicia los contenedores Mastodon.

$ systemctl restart mastodon.service

Sal del shell raíz.

$ exit

Si compruebas la página de salud de la base de datos, podrás ver si ahora hay alguna consulta lenta.

Mastodonte Comprobación de consultas lentas

Nota: También puedes iniciar las URL de PgHero y Sidekiq desde el menú Preferencias.

Si tu sitio no se carga por alguna razón, puedes comprobar los registros generados por Docker.

$ docker logs <container-name>

Paso 10 – Copia de seguridad de Mastodon

Utilizaremos una herramienta de terceros llamada Restic para hacer una copia de seguridad de Mastodon. El primer paso para hacer una copia de seguridad utilizando Restic es añadir todos los archivos y directorios a la lista de repositorios.

Crea y abre el archivo de la lista de repositorios para editarlo.

$ sudo nano /opt/mastodon/backup-files

Pega en él las siguientes líneas.

/etc/nginx
/etc/letsencrypt
/etc/systemd/system
/root
/opt/mastodon/database/pgbackups
/opt/mastodon/*.env
/opt/mastodon/docker-compose.yml
/opt/mastodon/branding
/opt/mastodon/database/redis
/opt/mastodon/web/system
/opt/mastodon/backup-files
/opt/mastodon/mastodon-backup

Guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te pida.

Instala Restic.

$ sudo apt install restic

Crea un repositorio de copia de seguridad y crea la copia de seguridad inicial. Vamos a hacer una copia de seguridad de nuestros datos en el servicio S3.

$ restic -r s3:https://$SERVER:$PORT/mybucket init
$ restic -r s3:https://$SERVER:$PORT/mybucket backup $(cat /opt/mastodon/backup-files) --exclude  /opt/mastodon/database/postgresql

Crea un temporizador de servicio de copia de seguridad Mastodon y ábrelo para editarlo.

$ sudo nano /etc/systemd/system/mastodon-backup.timer

Pega en él el siguiente código.

[Unit]
Description=Schedule a mastodon backup every hour

[Timer]
Persistent=true
OnCalendar=*:00:00
Unit=mastodon-backup.service

[Install]
WantedBy=timers.target

Guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te pida.

Crea un archivo de servicio de copia de seguridad de Mastodon y ábrelo para editarlo.

$ sudo nano /etc/systemd/system/mastodon-backup.service

Pega en él el siguiente código.

[Unit]
Description=Mastodon - backup service
# Without this, they can run at the same time and race to docker compose,
# double-creating networks and failing due to ambiguous network definition
# requiring `docker network prune` and restarting
After=mastodon.service

[Service]
Type=oneshot
StandardError=file:/var/log/mastodon-backup.err
StandardOutput=file:/var/log/mastodon-backup.log

WorkingDirectory=/opt/mastodon
ExecStart=/bin/bash /opt/mastodon/mastodon-backup

[Install]
WantedBy=multi-user.target

Guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te solicite.

A continuación, crea y abre el archivo /opt/mastodon/mastodon-backup para editarlo. Contiene los comandos de copia de seguridad propiamente dichos.

$ sudo nano /opt/mastodon/mastodon-backup

Pega en él el siguiente código.

#!/bin/bash

set -e

AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
SERVER=
PORT=
RESTIC_PASSWORD_FILE=/root/restic-pasword

docker compose -f /opt/mastodon/docker-compose.yml run --rm postgresql sh -c "pg_dump -Fp  mastodon | gzip > /backups/dump.sql.gz"
restic -r s3:https://$SERVER:$PORT/mybucket --cache-dir=/root backup $(cat /opt/mastodon/backup-files) --exclude  /opt/mastodon/database/postgresql
restic -r s3:https://$SERVER:$PORT/mybucket --cache-dir=/root forget --prune --keep-hourly 24 --keep-daily 7 --keep-monthly 3

Guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te solicite.

Da permisos de ejecución al script de copia de seguridad.

$ sudo chmod +x /opt/mastodon/mastodon-backup

Recarga el demonio de servicio e inicia el servicio de copia de seguridad y el temporizador.

$ sudo systemctl daemon-reload
$ sudo systemctl enable --now mastodon-backup.service
$ sudo systemctl enable --now mastodon-backup.timer

Confirma que se están realizando copias de seguridad cada hora y que se puede acceder a ellas mediante los siguientes comandos.

$ restic -r s3:https://$SERVER:$PORT/mybucket snapshots
$ restic -r s3:https://$SERVER:$PORT/mybucket mount /mnt

Paso 11 – Actualizar Mastodon

Actualizar Mastodon requiere varios pasos. Primero, cambia al directorio

$ cd /opt/mastodon

Extrae las últimas imágenes de contenedor para Mastodon.

$ docker compose pull

Realiza los cambios que quieras en docker-compose.yml.

Realiza todas las migraciones de bases de datos.

$ docker compose run --rm shell bundle exec rake db:migrate

Actualiza tus copias de archivos estáticos.

$ docker compose run --rm shell bash -c "cp -r /opt/mastodon/public/* /static/"

Reinicia los contenedores Mastodon.

$ sudo systemctl restart mastodon.service

Las instrucciones anteriores son instrucciones genéricas de actualización. Consulta siempre la página de versiones de GitHub de Mas todon para buscar tareas y comandos de actualización específicos entre versiones para asegurarte de que todo va bien.

Conclusión

Con esto concluye nuestro tutorial sobre la instalación de la Red Social Mastodon utilizando Docker en un servidor Debian 12. Si tienes alguna pregunta, publícala en los comentarios a continuación.

También te podría gustar...