Antes de leer esta página, si buscas si algo encaja en nuestra infraestructura, ve a Capacidad y límites. Esta página es la referencia técnica detallada del servidor.
| Componente | Detalle |
|---|---|
| Nombre | pmpserverubu |
| IP local | 192.168.1.10 |
| IP pública | 194.224.94.34 |
| Sistema operativo | Ubuntu Server 24.04 LTS |
| Dominio | pampl.ing (wildcard *.pampl.ing) |
| CPU | 40 cores |
| RAM | 219 GB |
| Disco sistema | 1.1 TB, LVM (ubuntu--vg-ubuntu--lv) — 100 GB iniciales extendidos el 21/04/2026 |
| RAID 5 | 1.67 TB útiles — ver RAID 5 |
| Disco backups | 558 GB, montado en /backups (deliberadamente fuera del RAID) |
Disco /data-social |
549 GB, dedicado a la app partners |
| Discos libres | 3 discos (558 + 223 + 223 GB) disponibles para ampliación |
Aviso importante: el servidor usa una controladora RAID de hardware (Dell MegaRAID) y los nombres
/dev/sdXno son estables entre reinicios. Verificado: un disco que erasdhpasó a sersditras un reinicio. Cualquier referencia a un disco debe hacerse por UUID o etiqueta, nunca por letra.
| Parámetro | Valor |
|---|---|
| Versión | 4.1.2 |
| Acceso panel | https://coolify.pampl.ing (usar siempre el dominio, no la IP) |
| Proxy | Traefik v3.6 |
| Certificados SSL | Let's Encrypt (TLS challenge) |
| Puerto HTTP | 80 |
| Puerto HTTPS | 443 |
| API | https://coolify.pampl.ing/api/v1 — ver guía de API |
A fecha de esta revisión hay ~142 contenedores desplegados, repartidos en múltiples proyectos de Coolify (IA Projects, Ecommerce, Crowdence, Wiki, entre otros). El listado detallado se consulta directamente en Coolify o vía API (GET /api/v1/applications), no se mantiene aquí porque cambia constantemente.
Migrados al RAID el 21/04/2026 para aprovechar redundancia y liberar el disco de sistema:
| Componente | Ubicación |
|---|---|
Docker data-root |
/data-raid/docker |
containerd root |
/data-raid/containerd |
Docker y containerd están retenidos (
apt-mark hold) desde la actualización del 03/08/2026, para evitar que una actualización automática rompa esta configuración. Actualizarlos requiere una ventana dedicada con verificación de que las rutas al RAID sobreviven.
⚠️ Corrección (19/08/2026): esta sección documentaba
PermitRootLogin no,PasswordAuthentication noyMaxAuthTries 3como la configuración real del servidor. Ese dato era falso. Verificado consudo sshd -Tel 19/08/2026, la configuración efectiva era otra:
Parámetro Documentado (falso) Real antes del cambio PermitRootLoginnowithout-passwordPasswordAuthenticationnoyesMaxAuthTries36(valor por defecto)Durante meses el puerto 22 estuvo abierto a internet con autenticación por contraseña habilitada para todos los usuarios, mientras esta página afirmaba lo contrario, dando una falsa sensación de seguridad. No restaures el bloque anterior: no era un ajuste que se perdiera, era información incorrecta desde el principio.
Causa raíz: en Ubuntu 24.04,
/etc/ssh/sshd_configincluye todos los ficheros de/etc/ssh/sshd_config.d/*.conf. Existía/etc/ssh/sshd_config.d/50-cloud-init.confcon la líneaPasswordAuthentication yes. Las directivas del fichero principal estaban comentadas, así que el valor efectivo lo imponía cloud-init en silencio.Lección permanente: nunca fiarse de
sshd_config. La configuración efectiva, tras procesar todos los includes, se obtiene siempre con:sudo sshd -T
Se crearon dos ficheros nuevos en /etc/ssh/sshd_config.d/. El orden de los nombres es intencionado: esos ficheros se procesan en orden alfabético y, en SSH, gana la primera aparición de cada directiva.
00-hardening.conf — se lee antes que 50-cloud-init.conf y lo sobrescribe. Un 99-hardening.conf no habría funcionado, porque cloud-init habría ganado por ir antes en el orden alfabético:
PasswordAuthentication no
PermitRootLogin prohibit-password
MaxAuthTries 3
99-adminpampling.conf — excepción, colocada a propósito en el último fichero. Un bloque Match afecta a todo lo que viene después hasta el siguiente Match o el final de la configuración; puesto al final, no captura por error directivas de otros ficheros:
Match User adminpampling Address 192.168.1.0/24
PasswordAuthentication yes
Por qué existe la excepción: el usuario adminpampling (cuenta del administrador de sistemas) no tiene clave SSH configurada, solo contraseña. Desactivar las contraseñas globalmente lo habría dejado fuera del servidor. La excepción le permite seguir entrando por contraseña, pero solo desde la red local (192.168.1.0/24): desde internet nadie puede autenticarse por contraseña, ni siquiera él.
Pendiente: cuando
adminpamplingtenga clave SSH configurada, retirar99-adminpampling.conf. El servidor quedará sin ninguna vía de contraseña.
sshd -T acepta simular el contexto de una conexión concreta, lo que permite comprobar el efecto de un cambio antes de aplicarlo:
sudo sshd -t # valida sintaxis; si falla, NO recargar
# Simular contextos concretos y comprobar el valor efectivo:
sudo sshd -T -C user=adminpampling,addr=192.168.1.230,host=x | grep -i passwordauthentication # debe dar yes
sudo sshd -T -C user=adminpampling,addr=8.8.8.8,host=x | grep -i passwordauthentication # debe dar no
sudo sshd -T -C user=josemoya,addr=192.168.1.165,host=x | grep -i passwordauthentication # debe dar no
sudo systemctl reload ssh # reload, no restart: no corta las conexiones existentes
Regla operativa: mantener siempre una sesión SSH abierta mientras se toca la configuración de SSH, para poder revertir si algo falla.
Coolify se conecta por SSH a su propio servidor como root para poder desplegar (construir imágenes, arrancar contenedores), usando la clave registrada en Coolify como localhost's key. Verificado: se conecta desde 10.0.1.128, IP de la red Docker coolify (subred 10.0.1.0/24), con fingerprint SHA256:/g3OgB1lU8jXdGu8quyhxqybbrhKDixTNUn0T3cN3B4.
Esa clave es la única entrada de /root/.ssh/authorized_keys, y hasta el 19/08/2026 era válida desde cualquier IP de internet.
Cambio aplicado: se añadieron opciones de restricción a esa entrada de authorized_keys:
from="10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1,fc00::/7,::1",no-port-forwarding,no-agent-forwarding,no-X11-forwarding ssh-ed25519 AAAA...
from= limita esa clave concreta (no el usuario root entero) a las redes indicadas. Se cubrió todo el espacio privado IPv4 e IPv6 en lugar de solo 10.0.1.128, porque los contenedores Docker cambian de IP al recrearse, y fijar la IP exacta habría dejado a Coolify sin poder desplegar tras una actualización. Se incluyó fc00::/7 porque la red Docker también tiene IPv6 (fdb0:97ee:c5d4::/64).no-* impiden usar la conexión como túnel o reutilizar el agente SSH.no-pty a propósito: Coolify podría necesitar terminal.authorized_keys se lee en cada conexión nueva, así que no hace falta recargar sshd: el efecto es inmediato./root/.ssh/authorized_keys.backup-<fecha>.Verificado tras el cambio: Coolify siguió conectando correctamente (Accepted publickey for root from 10.0.1.128) y un despliegue real terminó en Success.
⚠️ Esta misma clave aparece expuesta en los logs de deployment de Coolify (comportamiento de Coolify, no de esta restricción). Ver aviso de seguridad: logs de deployment de Coolify. El
from=reduce el impacto de una fuga —ya no sirve desde fuera de la red privada— pero no elimina el problema de fondo.
Activo, bloquea IPs con intentos fallidos de SSH.
sudo systemctl status fail2ban
sudo fail2ban-client status sshd
| Puerto | Acceso | Servicio |
|---|---|---|
| 22/tcp | Todo internet | SSH |
| 80/tcp | Todo internet | HTTP (apps públicas) |
| 443/tcp | Todo internet | HTTPS (apps públicas) |
| 53 | Solo red local | DNS interno (dnsmasq) |
| 6001-6002 | Solo red local | Coolify realtime |
| Puertos de BBDD | Solo red local, por app | PostgreSQL/otros vía Beekeeper |
| Todos | Solo red local | Cualquier servicio interno |
sudo ufw status verbose
Desde agosto de 2026, los tokens se emiten con permisos acotados por perfil de uso, en lugar de un único token con acceso total:
| Perfil | Permisos | Puede leer secretos/claves |
|---|---|---|
| Administración (sistemas) | root |
Sí |
| Equipo de desarrollo | read + write + deploy |
No |
El permiso read:sensitive es el que protege datos críticos: claves SSH privadas (que dan acceso root al servidor), valores de variables de entorno, y secretos de webhooks. Sin ese permiso, un token puede gestionar aplicaciones por completo pero no extraer esos datos.
Detalle completo en Coolify — Gestión de aplicaciones.
Regla: nunca escribir un token en el repositorio, en un .md o en un comando que quede en el historial. Se guardan en el gestor de credenciales del sistema o en la caja fuerte del equipo.
pampl.ing gestionado en Dondominio/Plesk.
pampl.ing → A → 194.224.94.34
*.pampl.ing → A → 194.224.94.34
| Puerto externo | Puerto interno | Destino |
|---|---|---|
| 80 | 80 | 192.168.1.10 |
| 443 | 443 | 192.168.1.10 |
Solo estos dos puertos están redirigidos.
El firewall corporativo no soporta hairpin NAT: un equipo de la red local no puede alcanzar la IP pública del propio firewall. Para evitarlo, dnsmasq resuelve todos los subdominios de pampl.ing directamente a 192.168.1.10.
/etc/dnsmasq.conf
address=/pampl.ing/192.168.1.10
server=8.8.8.8
server=8.8.4.4
listen-address=192.168.1.10,127.0.0.1
bind-interfaces
sudo systemctl restart dnsmasq
dig wiki.pampl.ing @192.168.1.10
Esto se rompe con facilidad y es la causa más frecuente de incidencias de acceso:
192.168.1.10 como DNS primarioSíntoma característico: la app no carga en el navegador normal pero sí en modo incógnito, o funciona para una persona y para otra no. Ver la sección de solución de problemas.
El servicio dnsmasq está configurado para esperar a que la red esté lista y reintentar si falla al arrancar (After=network-online.target, Restart=on-failure), tras el incidente del corte de luz del 10/04/2026.
Al no usar systemd-resolved, los contenedores necesitan DNS explícito:
// /etc/docker/daemon.json
{
"dns": ["192.168.1.10", "8.8.8.8", "8.8.4.4"],
"data-root": "/data-raid/docker",
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "3" }
}
sudo systemctl restart docker
sudo docker exec coolify-proxy nslookup acme-v02.api.letsencrypt.org
Let's Encrypt vía Traefik, método TLS challenge.
# /data/coolify/proxy/docker-compose.yml
- '--certificatesresolvers.letsencrypt.acme.tlschallenge=true'
- '--certificatesresolvers.letsencrypt.acme.storage=/traefik/acme.json'
- '--certificatesresolvers.letsencrypt.acme.email=jose.moya@pampling.com'
- '--entrypoints.https.http.tls.certResolver=letsencrypt'
Este es un problema recurrente documentado en varias incidencias. Cuando Coolify se actualiza, regenera docker-compose.yml de Traefik desde cero y pierde:
ERR_CONNECTION_TIMED_OUT en Chrome/Edge — ver incidencia HTTP/3)Alt-Svc: cleartlschallenge (revierte a HTTP challenge, que falla porque Coolify intercepta las peticiones ACME)--metrics.prometheus, puerto :8082)Tras cada actualización de Coolify, verificar y reaplicar estos ajustes. No hay forma de hacerlos permanentes desde la UI de Coolify.
# Verificar certificado servido
echo | openssl s_client -connect 192.168.1.10:443 -servername analytics.pampl.ing 2>/dev/null | openssl x509 -noout -issuer
# Ver si anuncia HTTP/3 (no debería)
curl -skI https://analytics.pampl.ing | grep -i alt-svc
sudo docker ps --format '{{.Names}}: {{.Image}}'
sudo docker stats --no-stream
Docker y containerd están en apt-mark hold (ver §2). Cualquier actualización requiere quitar el hold, actualizar, y verificar que daemon.json y containerd/config.toml siguen apuntando al RAID.
Ver Bitbucket — Control de versiones para el detalle completo.
Resumen:
.gitignore obligatorio: excluir .env, bases de datos, node_modules/, ficheros >100 MBVer RAID 5 — Configuración y mantenimiento para el detalle completo (creación, discos, monitorización, gestión de fallos).
| Ruta | Para qué | ¿Backup? |
|---|---|---|
/data-raid/apps/<app>/ |
Datos propios irreproducibles | Sí |
/data-social/ |
Contenido voluminoso reproducible (hoy: partners) |
No |
/mnt/<nombre>/ |
Montajes de red (CIFS/SMB) a otros servidores | No |
| Montaje | Origen | Para qué app | Documentación |
|---|---|---|---|
/mnt/marketing-finales |
//192.168.1.19/Marketing |
catalogo-odoo |
SMB Marketing |
/mnt/camaras-fotos |
//192.168.1.19/CamarasFotos |
camaras-web |
ver página de servicios |
Todos los montajes CIFS usan soft en vez de las opciones por defecto (hard): si el servidor de origen deja de responder, la lectura falla con error en lugar de dejar el proceso colgado indefinidamente, lo que podría bloquear el contenedor entero.
df -h /data-raid /data-social /mnt/marketing-finales /mnt/camaras-fotos
/dev/sda (558 GB), ext4, montado en /backups. Deliberadamente en un disco fuera del RAID, para que un fallo del RAID no se lleve también los backups.
| Parámetro | Valor |
|---|---|
| Script | /usr/local/bin/backup-coolify.sh |
| Frecuencia | Diaria a las 03:00 |
| Retención | 15 días |
| Log | /var/log/backup-coolify.log |
| Estado estructurado | /var/lib/backups/status.json |
coolify.type=database (no por imagen — el filtro anterior por imagen solo detectaba 2 de 8 BBDD existentes; se corrigió en abril de 2026). Soporta PostgreSQL, MySQL/MariaDB y MongoDB/data/coolify/source/.env, proxy/, ssh//data/coolify/databases/, services/, applications//data-raid/apps/ — ver la advertencia en Capacidad y límites §2El script notifica automáticamente al server-monitor si detecta fallos (parcial o total).
Los logs de contenedores archivados por
docker-log-archiverno entran en este backup a propósito — viven en/data-raid/logs-archive, fuera del scope. Ver Archivador de logs de contenedores.
ls -lh /backups/
cat /var/lib/backups/status.json | jq
tail -30 /var/log/backup-coolify.log
sudo /usr/local/bin/backup-coolify.sh # ejecución manual
gunzip /backups/FECHA/NOMBRE_CONTENEDOR_db.sql.gz
cat /backups/FECHA/NOMBRE_CONTENEDOR_db.sql | docker exec -i NOMBRE_CONTENEDOR psql -U USUARIO
Es una decisión consciente, no un olvido. Los backups protegen contra fallo de disco o error humano, no contra pérdida física del servidor. Ver Capacidad y límites §3.5.
server-monitor.pampl.ing recibe alertas de scripts del servidor vía webhook (POST /api/alerts/receive, con X-API-Key).
Fuentes que ya notifican:
monitor-raid.sh, cada 5 min, solo avisa en cambios de estado)Además, Traefik expone métricas Prometheus en http://coolify-proxy:8082/metrics (solo red interna Docker) con latencias y códigos de respuesta por aplicación.
Estado de salud de las aplicaciones: de 91 aplicaciones, solo 10 tienen healthcheck configurado. Ver Capacidad y límites §3.3.
sudo adduser nombreusuario
sudo mkdir -p /home/nombreusuario/.ssh
echo "CLAVE_PUBLICA" >> /home/nombreusuario/.ssh/authorized_keys
sudo chown -R nombreusuario:nombreusuario /home/nombreusuario/.ssh
sudo chmod 700 /home/nombreusuario/.ssh
sudo chmod 600 /home/nombreusuario/.ssh/authorized_keys
sudo usermod -aG docker nombreusuario # si necesita gestionar contenedores
Recordatorio: dentro de un team, todos los miembros ven y pueden modificar todos los recursos de ese team. No hay permisos por aplicación.
| Recurso | Acceso |
|---|---|
| SSH | ssh josemoya@192.168.1.10 |
| Panel Coolify | https://coolify.pampl.ing |
| API Coolify | https://coolify.pampl.ing/api/v1 |
| Wiki | https://wiki.pampl.ing |
| Server monitor | https://server-monitor.pampl.ing |
| Bitbucket | bitbucket.org/pampling/ |
Es el problema de DNS del §4. Antes de investigar la aplicación:
nslookup analytics.pampl.ing # ejecutar en el PC afectado
Si resuelve a 194.224.94.34 (IP pública) en vez de 192.168.1.10, es el DNS del cliente (revisar DNS-over-HTTPS en el navegador, configuración manual, o VPN). No es un problema de la aplicación ni del servidor.
sudo systemctl restart docker
Verificar "dns" en /etc/docker/daemon.json.
.ingPrimero comprobar si Coolify revirtió la configuración de Traefik (§5). Si el certificado es correcto pero Chrome sigue bloqueando: el TLD .ing está en la lista de precarga HSTS de Chrome.
chrome://net-internals/#hsts
→ "Delete domain security policies" → escribir el dominio → Delete → recargar.
sudo lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv
sudo resize2fs /dev/ubuntu-vg/ubuntu-lv
sudo docker rm NOMBRE_DEL_CONTENEDOR
Verificar que los puertos 6001-6002 son accesibles desde el equipo que usa Coolify.
No es un problema del servicio ni de descriptores de fichero normales: son las instancias de inotify agotadas. Ver §14 (Configuración del kernel — inotify) para diagnóstico y solución.
Docker borra los logs junto con el contenedor y la API de Coolify no los sirve si la app no está corriendo (400 Application is not running). Desde el 19/08/2026 hay un archivo paralelo que sobrevive a la destrucción del contenedor — ver Archivador de logs de contenedores.
Comprobación estándar tras cualquier reinicio (ha hecho falta en todos los reinicios documentados):
docker ps -a --filter "status=exited" --format "{{.Names}}" # suele aparecer coolify-sentinel
docker start coolify-sentinel
systemctl status dnsmasq
docker info --format '{{.DockerRootDir}}' # debe ser /data-raid/docker
sudo tcpdump -i eno4 port 443 -c 5
# y acceder desde un móvil con datos (no WiFi) a https://pampl.ing
Los parámetros de kernel que necesitan tocarse en este servidor se guardan en ficheros propios dentro de /etc/sysctl.d/ (uno por parámetro o grupo relacionado), nunca editando /etc/sysctl.conf directamente. Así sobreviven a reinicios y quedan documentados por separado.
Síntoma detectado: al arrancar un servicio nuevo de systemd (docker-log-archiver.service), systemd devolvió:
Failed to allocate directory watch: Too many open files
El servicio arrancó igualmente pese al aviso — no era un fallo del servicio, sino un síntoma del sistema.
Diferencia clave (la confusión habitual):
| Parámetro | Qué limita | Valor en este servidor |
|---|---|---|
fs.inotify.max_user_watches |
Cuántos ficheros/directorios individuales puede vigilar un usuario en total, sumando todas sus instancias de inotify | 1048576 (holgado) |
fs.inotify.max_user_instances |
Cuántos canales (file descriptors) de inotify puede abrir un usuario — cada proceso que vigila algo (systemd, Docker, containerd, un editor con recarga en caliente...) consume una instancia | era 128 (por defecto de Ubuntu), ahora 1024 |
El mensaje de error de systemd es engañoso: dice "Too many open files" pero no se refiere a descriptores de fichero normales (ulimit -n), sino específicamente a instancias de inotify agotadas.
Diagnóstico realizado:
# Límites actuales
cat /proc/sys/fs/inotify/max_user_watches
cat /proc/sys/fs/inotify/max_user_instances
# Instancias de inotify realmente en uso (requiere sudo)
sudo find /proc/*/fd -lname anon_inode:inotify 2>/dev/null | wc -l
Resultado: max_user_watches en 1048576 (no era el problema), max_user_instances en 128 con 135 instancias ya en uso — el límite estaba rebasado. Causa de fondo: la densidad de contenedores del servidor (~142 en el momento del incidente). systemd, Docker y containerd abren una instancia de inotify por cada componente que vigila cambios en ficheros, y 128 es un valor pensado para un escritorio, no para un servidor con esta carga de contenedores.
Por qué importa: cuando se agotan las instancias de inotify, los procesos que necesitan vigilar ficheros fallan de forma silenciosa — no siempre con un error visible. Puede afectar a systemd (no detecta cambios en unidades), a Docker/containerd, y a aplicaciones con recarga en caliente. Es un límite que produce fallos raros y difíciles de atribuir a su causa real, porque el mensaje apunta a "ficheros abiertos" en vez de a inotify.
Solución aplicada — fichero /etc/sysctl.d/99-inotify.conf:
fs.inotify.max_user_instances = 1024
sudo sysctl --system
Persistente: al vivir en /etc/sysctl.d/, sobrevive a reinicios.
A vigilar: el número de instancias en uso crecerá si crece el número de contenedores del servidor. Si al repetir el diagnóstico el uso se acerca de nuevo al límite (1024), hay que volver a subir
max_user_instancesen el mismo fichero.