Cómo instalar la red social Mastodon con Docker en Rocky Linux 9

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 Rocky Linux 9 utilizando Docker. Docker facilita la instalación de Mastodon al contener todos los paquetes y servicios necesarios en contenedores.

Requisitos previos

  • Un servidor con Rocky Linux 9 con un mínimo de 2 núcleos de CPU y 2 GB de memoria. Tendrás que actualizar el servidor según tus necesidades.
  • 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 dnf update
    
  • Instala los paquetes de utilidades básicas. Puede que algunos ya estén instalados.
    $ sudo dnf install wget curl nano unzip yum-utils -y
    

Paso 1 – Configurar el cortafuegos

El primer paso es configurar el cortafuegos. Rocky Linux utiliza el cortafuegos Firewalld. Comprueba el estado del cortafuegos.

$ sudo firewall-cmd --state
running

El cortafuegos funciona con diferentes zonas, y la zona pública es la que utilizaremos por defecto. Enumera todos los servicios y puertos activos en el cortafuegos.

$ sudo firewall-cmd --permanent --list-services

Debería mostrar la siguiente salida.

cockpit dhcpv6-client ssh

Wiki.js necesita los puertos HTTP y HTTPS para funcionar. Ábrelos.

$ sudo firewall-cmd --permanent --add-service=http
$ sudo firewall-cmd --permanent --add-service=https

Añade enmascaramiento, ya que la aplicación contactará con otras instancias.

$ sudo firewall-cmd --permanent --add-masquerade

Recarga el cortafuegos para aplicar los cambios.

$ sudo firewall-cmd --reload

Paso 2 – Instalar Docker y Docker Compose

Rocky Linux viene con una versión antigua de Docker. Para instalar la última versión, instala primero el repositorio oficial de Docker.

$ sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

Instala la última versión de Docker.

$ sudo dnf install docker-ce docker-ce-cli containerd.io

Es posible que obtengas el siguiente error al intentar instalar Docker.

ror: 
 Problem: problem with installed package buildah-1:1.26.2-1.el9_0.x86_64
  - package buildah-1:1.26.2-1.el9_0.x86_64 requires runc >= 1.0.0-26, but none of the providers can be installed
  - package containerd.io-1.6.9-3.1.el9.x86_64 conflicts with runc provided by runc-4:1.1.3-2.el9_0.x86_64
  - package containerd.io-1.6.9-3.1.el9.x86_64 obsoletes runc provided by runc-4:1.1.3-2.el9_0.x86_64
  - cannot install the best candidate for the job

Utiliza el siguiente comando si te aparece el error anterior.

$ sudo dnf install docker-ce docker-ce-cli containerd.io docker-compose-plugin --allowerasing

Habilita y ejecuta el demonio Docker.

$ sudo systemctl enable docker --now

Comprueba que se está ejecutando.

? docker.service - Docker Application Container Engine
     Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)
     Active: active (running) since Sat 2022-11-12 00:19:44 UTC; 6s ago
TriggeredBy: ? docker.socket
       Docs: https://docs.docker.com
   Main PID: 99263 (dockerd)
      Tasks: 8
     Memory: 28.1M
        CPU: 210ms
     CGroup: /system.slice/docker.service
             ??99263 /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)

Tendrás 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 está añadido al grupo Docker.

$ groups
navjot wheel 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.

$ 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
$ sudo sysctl --load /etc/sysctl.d/90-max_map_count.conf

Configura SELinux para permitir las conexiones de red.

$ sudo setsebool -P httpd_can_network_connect 1

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 la propiedad de 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.

version: '3'

services:
  postgresql:
    image: postgres:15-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.7
    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
    ulimits:
      memlock:
        soft: -1
        hard: -1
    healthcheck:
      test: ["CMD-SHELL", "nc -z elasticsearch 9200"]
    volumes:
      - elasticsearch:/usr/share/elasticsearch/data
    networks:
      - internal_network

  website:
    image: tootsuite/mastodon:v4.0.2
    env_file:
      - application.env
      - database.env
    command: bash -c "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.0.2
    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.0.2
    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.0.2
    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.0.2. 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.x. No hay una versión principal de Elasticsearch que puedas seguir en la página de Docker Hub, así que tendrás que seguir actualizándola manualmente para las actualizaciones de seguridad relacionadas con Java.

Ese internal: true no funciona con firewalld, por eso está comentado en el archivo anterior. Si esto se arregla alguna vez, podrás volver a añadir esa restricción adicional.

Crear secretos de aplicación

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

Genera los valores SECRET_KEY_BASE y OTP_SECRET ejecutando dos veces el siguiente comando. La primera vez llevará algún tiempo, ya que extraerá las imágenes.

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

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

$ openssl rand -hex 64

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

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=AKIA3FIG4NVFNSC3AHXE
SMTP_PASSWORD=BHM4MVOjBmnGhSJ9lH3PAXKJ/9AiLWcUghG/kEN2kkFo
[email protected]

# secrets
SECRET_KEY_BASE=c09fa403575e0b431e54a2e228f20cd5a5fdfdbba0da80598959753b829a4e3c0266eedbac7e3cdf9f3345db36c56302c0e1bc5bfc8c5d516be59a2c41de7e37
OTP_SECRET=febb7dbb0d3308094083733fc923a430e52ccec767d48d7d2e0c577bfcb6863dbdfc920b1004b1f8c2967b9866bd7a0b4a15460f9fc7687aa4a42acf54e5a3d4

# Changing VAPID keys will break push notifications
VAPID_PRIVATE_KEY=13RgrfOY2tkwuUycylDPOkoHennkJ0ZAPV_fUwDy7-g=
VAPID_PUBLIC_KEY=BDAQuGwPbh1kbCV904adYXHvz9lLRaJHkiQkihRDPyBn3QmkAYbR21WHYoP8TkyG6dylG6IXpEVfLwdoW7fJVns=

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

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=15ff12dcb93aa60680d2aadb4032ee
PGPASSWORD=15ff12dcb93aa60680d2aadb4032ee
PGPORT=5432
PGHOST=postgresql
PGUSER=mastodon

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

# elasticsearch
ES_JAVA_OPTS=-Xms512m -Xmx512m
ELASTIC_PASSWORD=13382e99f6b2d4dc7f3d66e4b9872d

# mastodon database configuration
#DB_HOST=pgbouncer
DB_HOST=postgresql
DB_USER=mastodon
DB_NAME=mastodon
DB_PASS=15ff12dcb93aa60680d2aadb4032ee
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=13382e99f6b2d4dc7f3d66e4b9872d

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

Prepara Mastodon

Prepara los archivos estáticos para que los sirva Nginx.

$ 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

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

Paso 5 – Instalar Nginx

Rocky Linux viene con una versión antigua de Nginx. Necesitas descargar el repositorio oficial de Nginx para instalar la última versión.

Crea y abre el archivo /etc/yum.repos.d/nginx.repo para crear el repositorio oficial de Nginx.

$ sudo nano /etc/yum.repos.d/nginx.repo

Pega en él el siguiente código.

[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

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

Instala el servidor Nginx.

$ sudo dnf install nginx

Verifica la instalación.

$ nginx -v
nginx version: nginx/1.22.1

Habilita e inicia el servidor Nginx.

$ sudo systemctl enable nginx --now

Comprueba el estado del servidor.

$ sudo systemctl status nginx
? nginx.service - nginx - high performance web server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)
     Active: active (running) since Sun 2022-11-13 13:49:55 UTC; 1s ago
       Docs: http://nginx.org/en/docs/
    Process: 230797 ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf (code=exited, status=0/SUCCESS)
   Main PID: 230798 (nginx)
      Tasks: 3 (limit: 12355)
     Memory: 2.8M
        CPU: 13ms
     CGroup: /system.slice/nginx.service
             ??230798 "nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf"
             ??230799 "nginx: worker process"
             ??230800 "nginx: worker process"

Paso 6 – Instalar SSL

La herramienta Certbot genera certificados SSL utilizando la API Let’s Encrypt. Requiere el repositorio EPEL para funcionar.

$ sudo dnf install epel-release

Utilizaremos Snapd para instalar Certbot. Instala Snapd.

$ sudo dnf install snapd

Activa e Inicia el servicio Snap.

$ sudo systemctl enable snapd --now

Instala el paquete central de Snap.

$ sudo snap install core
$ sudo snap refresh core

Crea los enlaces necesarios para que Snapd funcione.

$ sudo ln -s /var/lib/snapd/snap /snap
$ echo 'export PATH=$PATH:/var/lib/snapd/snap/bin' | sudo tee -a /etc/profile.d/snapd.sh

Ejecuta el siguiente comando para instalar Certbot.

$ sudo snap install --classic certbot

Habilita Certbot creando el enlace simbólico a su ejecutable.

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

Genera el 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

Para comprobar si la renovación SSL funciona correctamente, realiza una ejecución en seco del proceso.

$ 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 http2;
   server_name mastodon.example.com;
   
   access_log  /var/log/nginx/mastodon.access.log;
   error_log   /var/log/nginx/mastodon.error.log;

   http2_push_preload on; # Enable HTTP/2 Server Push

   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

Si aparece el siguiente error, lo más probable es que se deba a Restricciones de SELinux.

nginx: [emerg] open() "/var/run/nginx.pid" failed (13: Permission denied)

Para solucionar el error, ejecuta los siguientes comandos.

$ sudo ausearch -c 'nginx' --raw | audit2allow -M my-nginx
$ sudo semodule -X 300 -i my-nginx.pp

Inicia de nuevo el servicio Nginx.

$ sudo systemctl start 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

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 admin
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-web-1 /bin/bash

Ejecuta el siguiente comando.

$ sed -E 's/indices.sum.+/2000/g' -i lib/mastodon/search_cli.rb

Sal del intérprete de comandos del contenedor.

$ exit

Ejecuta de nuevo el comando de despliegue de Elasticsearch.

$ tootctl search deploy

Servicios auxiliares adicionales

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

Crea y abre el servicio de eliminación de archivos multimedia 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

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 2 usuarios y 1 de ellos (yo) está configurado como administrador. Este no suele ser el caso. 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 sitio 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 dnf 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 Rocky Linux 9. Si tienes alguna pregunta, publícala en los comentarios a continuación.

También te podría gustar...