Exploring the roots and future of Internet Relay Chat

Docker para servidores IRC: guía completa con docker-compose

Llevaba 15 años desplegando servidores IRC directamente en el sistema operativo. Compilar UnrealIRCd, resolver dependencias de OpenSSL, pelear con rutas de bibliotecas, rezar para que la actualización del sistema no rompiera nada. En 2019, un colega me convenció de probar Docker para un ircd de test. Tardé 10 minutos en tener UnrealIRCd funcionando. Desde entonces, todos mis servidores IRC van en contenedores.

Por qué Docker para IRC

La ventaja principal es el aislamiento. Tu servidor IRC no depende de las bibliotecas del host. No importa si el host tiene OpenSSL 1.1 o 3.0 — dentro del contenedor tienes exactamente la versión que necesitas. Esto elimina la categoría entera de problemas "funciona en mi máquina pero no en producción".

La segunda ventaja es la reproducibilidad. Un docker-compose.yml define tu infraestructura entera. Puedes levantar un servidor idéntico en otra máquina con un solo comando. Migrar de proveedor de hosting es copiar archivos y ejecutar docker compose up. Nada más.

Tercera ventaja: actualizaciones limpias. Para actualizar el ircd, cambias la etiqueta de imagen y recreas el contenedor. Si algo falla, vuelves a la etiqueta anterior. Sin compilaciones, sin conflictos de dependencias, sin rezar. Es como tener un botón de "deshacer" para tu infraestructura.

Para empresas que gestionan infraestructura de comunicaciones, este enfoque basado en contenedores reduce drásticamente los tiempos de despliegue y mantenimiento. Proveedores de servicios IT como PowerITi llevan años aplicando estas metodologías de contenedores a la infraestructura de sus clientes PYME, y los resultados en estabilidad y velocidad de recuperación ante incidentes son evidentes.

Arquitectura de contenedores para IRC

Un servidor IRC en Docker tiene tres componentes principales: el ircd en sí, los volúmenes para datos persistentes, y la red. Vamos a ver cómo encaja todo.

# Estructura de directorios recomendada
/opt/irc/
├── docker-compose.yml          # Definición de servicios
├── unrealircd/
│   ├── conf/                   # Configuración del ircd
│   │   ├── unrealircd.conf     # Config principal
│   │   └── modules.conf        # Módulos a cargar
│   ├── tls/                    # Certificados TLS
│   │   ├── server.cert.pem
│   │   └── server.key.pem
│   ├── data/                   # Datos persistentes
│   └── logs/                   # Logs del servidor
├── services/
│   ├── conf/                   # Config de Anope/Atheme
│   └── data/                   # Bases de datos de servicios
└── letsencrypt/                # Certbot data

Los volúmenes son el punto más delicado. La configuración, los certificados TLS, las bases de datos de servicios y los logs deben vivir fuera del contenedor. Si no montas estos directorios como volúmenes, los pierdes cada vez que recreas el contenedor. Es un error que todo el mundo comete una vez.

Para la red, Docker crea una red bridge por defecto. Dentro de esa red, los contenedores se resuelven por nombre. Si tienes un contenedor llamado unrealircd y otro llamado anope, Anope puede conectar al ircd simplemente usando unrealircd como hostname. Sin IPs hardcodeadas, sin trucos de DNS.

docker-compose: UnrealIRCd paso a paso

Este es un compose funcional para UnrealIRCd con Anope como servicios de red.

# docker-compose.yml
version: "3.8"

services:
  unrealircd:
    image: unrealircd/unrealircd:6.1.4
    container_name: unrealircd
    restart: unless-stopped
    ports:
      - "6667:6667"     # IRC sin TLS
      - "6697:6697"     # IRC con TLS
      - "8067:8067"     # WebSocket
    volumes:
      - ./unrealircd/conf:/data/unrealircd/conf
      - ./unrealircd/tls:/data/unrealircd/tls:ro
      - ./unrealircd/data:/data/unrealircd/data
      - ./unrealircd/logs:/data/unrealircd/logs
    networks:
      - ircnet
    healthcheck:
      test: ["CMD", "nc", "-z", "localhost", "6667"]
      interval: 30s
      timeout: 5s
      retries: 3

  anope:
    image: anope/anope:2.0
    container_name: anope
    restart: unless-stopped
    depends_on:
      unrealircd:
        condition: service_healthy
    volumes:
      - ./services/conf:/data/anope/conf
      - ./services/data:/data/anope/data
    networks:
      - ircnet

networks:
  ircnet:
    driver: bridge

Puntos a notar. depends_on con condition: service_healthy garantiza que Anope no intente conectar hasta que UnrealIRCd esté listo. Sin esto, Anope se inicia antes de que el ircd acepte conexiones y falla. restart: unless-stopped reinicia automáticamente el contenedor si se cae, excepto si lo paras manualmente.

El volumen de TLS está montado como :ro (read-only). El ircd solo necesita leer los certificados, no escribir en ese directorio. Principio de mínimo privilegio aplicado a volúmenes.

# Comandos básicos de gestión
docker compose up -d                # Levantar todo en background
docker compose logs -f unrealircd   # Ver logs en tiempo real
docker compose restart unrealircd   # Reiniciar solo el ircd
docker compose down                 # Parar todo
docker compose pull                 # Descargar imágenes actualizadas

# Recargar config sin reiniciar el contenedor
docker compose exec unrealircd kill -HUP 1

# Conectar al shell del contenedor para diagnóstico
docker compose exec unrealircd /bin/sh
Consejo: Siempre especifica la versión exacta de la imagen (ej: 6.1.4), nunca uses latest. Con latest, un docker compose pull puede traer una versión nueva que rompa tu configuración. Con versión fija, tú decides cuándo actualizar.

docker-compose: Ergo (todo en uno)

Ergo (antes Oragono) es ideal para Docker porque incluye servicios integrados. No necesitas Anope ni Atheme. Un solo contenedor con todo: ircd, NickServ, ChanServ, y soporte IRCv3 completo.

# docker-compose.yml para Ergo
version: "3.8"

services:
  ergo:
    image: ghcr.io/ergochat/ergo:v2.13.1
    container_name: ergo
    restart: unless-stopped
    ports:
      - "6667:6667"
      - "6697:6697"
      - "8097:8097"    # WebSocket TLS
    volumes:
      - ./ergo/ircd.yaml:/ircd/ircd.yaml
      - ./ergo/tls:/ircd/tls:ro
      - ./ergo/db:/ircd/db
      - ./ergo/logs:/ircd/logs
    networks:
      - ircnet
    healthcheck:
      test: ["CMD", "nc", "-z", "localhost", "6667"]
      interval: 30s
      timeout: 5s
      retries: 3
    mem_limit: 512m
    cpus: 1.0

networks:
  ircnet:
    driver: bridge

Fíjate en mem_limit y cpus. Limitan los recursos del contenedor. Un servidor IRC pequeño no necesita más de 512 MB de RAM ni un core completo. Si no pones límites, un ataque de flood podría consumir todos los recursos del host y afectar a otros servicios.

La configuración de Ergo va en un solo archivo YAML, que es mucho más legible que la configuración de UnrealIRCd o InspIRCd. Lo montas como volumen y listo.

TLS con Let's Encrypt y renovación automática

TLS es obligatorio. No hay discusión. Un servidor IRC sin TLS expone contraseñas, mensajes y metadatos de conexión. Let's Encrypt te da certificados gratuitos con renovación automática.

# Obtener certificado inicial (ejecutar en el host)
sudo certbot certonly --standalone \
  -d irc.tudominio.com \
  --agree-tos \
  --email admin@tudominio.com

# Los certificados quedan en:
# /etc/letsencrypt/live/irc.tudominio.com/fullchain.pem
# /etc/letsencrypt/live/irc.tudominio.com/privkey.pem

# Script de renovación + copia + recarga
#!/bin/bash
# /opt/irc/renew-tls.sh
certbot renew --quiet

# Copiar al directorio del ircd
cp /etc/letsencrypt/live/irc.tudominio.com/fullchain.pem \
   /opt/irc/unrealircd/tls/server.cert.pem
cp /etc/letsencrypt/live/irc.tudominio.com/privkey.pem \
   /opt/irc/unrealircd/tls/server.key.pem

# Recargar configuración del ircd
cd /opt/irc && docker compose exec unrealircd kill -HUP 1

# Cron: ejecutar cada día a las 3am
# 0 3 * * * /opt/irc/renew-tls.sh

El truco está en recargar el ircd después de copiar los certificados nuevos. kill -HUP 1 envía SIGHUP al proceso principal del contenedor, que en la mayoría de ircds causa una recarga de configuración sin desconectar a los usuarios.

Advertencia: No montes /etc/letsencrypt directamente como volumen en el contenedor. Los certificados de Let's Encrypt usan symlinks que Docker no resuelve correctamente en todos los casos. Copia los archivos reales al directorio de volumen del ircd. Es un paso extra pero evita problemas difíciles de diagnosticar.

Alternativa: Podman sin daemon

Podman es una alternativa a Docker que no requiere un daemon corriendo como root. Cada contenedor es un proceso normal del usuario. Para servidores IRC esto tiene una ventaja de seguridad clara: si alguien compromete el contenedor, no tiene acceso al daemon Docker y por tanto no puede controlar otros contenedores.

# Podman usa los mismos comandos que Docker
# Solo cambia 'docker' por 'podman'
podman compose up -d
podman compose logs -f
podman compose down

# Generar servicio systemd desde el contenedor
podman generate systemd --new --name ergo > \
  ~/.config/systemd/user/container-ergo.service
systemctl --user enable container-ergo.service
systemctl --user start container-ergo.service

# Habilitar linger para que los contenedores persistan
# después de cerrar sesión
loginctl enable-linger $USER

Podman puede ejecutar los mismos archivos docker-compose.yml sin modificación. La compatibilidad es casi total. He migrado servidores IRC de Docker a Podman sin cambiar una línea de configuración.

La integración con systemd es mejor en Podman. Puedes generar unidades systemd automáticamente desde contenedores existentes. Esto te da las ventajas de systemd (logs centralizados, arranque automático, dependencias) sin perder las ventajas de contenedores.

Checklist de producción

Antes de poner un servidor IRC en Docker en producción, verifica estos puntos. No son opcionales.

# Checklist de producción para IRC en Docker

[x] Versiones de imagen fijadas (nunca :latest)
[x] TLS habilitado en puerto 6697
[x] Volúmenes para: config, TLS, datos, logs
[x] TLS con volumen :ro (read-only)
[x] Healthcheck configurado
[x] Límites de memoria y CPU
[x] restart: unless-stopped
[x] depends_on con condition para servicios
[x] Backup automático de volúmenes de datos
[x] Renovación automática de certificados TLS
[x] Logs con rotación (evitar disco lleno)
[x] Firewall: solo puertos necesarios abiertos
[x] Puerto 6667 cerrado si solo permites TLS

El backup de volúmenes es lo que más se olvida. Los datos de Anope o Ergo contienen las cuentas de usuarios, canales registrados, y configuración acumulada. Perder eso significa empezar de cero. Un tar czf diario del directorio de datos es lo mínimo.

# Backup diario de datos IRC
#!/bin/bash
# /opt/irc/backup.sh
FECHA=$(date +%Y%m%d)
BACKUP_DIR=/opt/backups/irc

mkdir -p $BACKUP_DIR

# Parar servicios brevemente para consistencia
cd /opt/irc
docker compose stop anope
tar czf $BACKUP_DIR/irc-data-$FECHA.tar.gz \
  ./services/data ./unrealircd/data
docker compose start anope

# Mantener solo los últimos 30 backups
find $BACKUP_DIR -name "irc-data-*.tar.gz" -mtime +30 -delete

Para monitorización, Docker expone métricas que puedes recoger con Prometheus. Memoria, CPU, red, estado del contenedor — todo disponible sin agente adicional dentro del contenedor. Combínalo con Grafana y tienes un dashboard completo de tu infraestructura IRC.

Docker ha cambiado cómo mantengo servidores IRC. Lo que antes requería una tarde de compilación y configuración ahora se hace en minutos. Y cuando algo falla, volver atrás es cuestión de cambiar un tag de imagen y recrear. No es magia — es infraestructura como código aplicada a IRC.

Preguntas frecuentes

¿Docker añade latencia al servidor IRC?

La latencia adicional de Docker es insignificante para IRC. Estamos hablando de microsegundos en la capa de red bridge. IRC es un protocolo de texto donde la latencia relevante se mide en milisegundos. No vas a notar ninguna diferencia entre un ircd nativo y uno en Docker. El overhead de CPU y memoria también es mínimo — Docker comparte el kernel del host, no es una VM.

¿Puedo enlazar servidores IRC en diferentes hosts con Docker?

Sí. Para enlaces entre servidores (server linking), expones el puerto de enlace en el docker-compose y configuras el ircd para aceptar conexiones entrantes. El enlace funciona exactamente igual que sin Docker — es una conexión TCP normal entre dos ircds. Solo asegúrate de que el firewall permite el tráfico entre los hosts y que usas TLS para el enlace.

¿Docker o Podman para IRC en producción?

Ambos funcionan perfectamente. Podman tiene ventajas de seguridad (sin daemon root) y mejor integración con systemd. Docker tiene mayor ecosistema y documentación. Si tu equipo ya usa Docker, quédate con Docker. Si empiezas de cero y la seguridad es prioridad, Podman es la mejor opción. La migración entre ambos es trivial.