Qué cambia al usar Docker en un VPS
Docker en un VPS usa el mismo motor, pero con menos margen: la RAM se agota, UFW no bloquea puertos publicados, los contenedores no vuelven y el disco se llena.
Qué cambia cuando ejecuta Docker en un VPS
Docker en un VPS utiliza el mismo motor y las mismas imágenes que Docker en su equipo, por lo que todos los comandos que ya conoce siguen funcionando. Lo que cambia es el margen disponible. Un equipo suele tener memoria libre, un firewall que nadie analiza y un disco lo bastante grande como para no revisarlo nunca. Un servidor alquilado tiene un límite fijo de memoria, una dirección IP pública que recibe análisis a los pocos minutos de arrancar y un sistema de archivos raíz que Docker llenará sin pedir confirmación.
Cuatro diferencias causan la mayoría de los problemas en un servidor pequeño:
- La memoria es limitada y el kernel resuelve la falta de memoria terminando un proceso.
- Un puerto publicado atraviesa directamente UFW (uncomplicated firewall), porque Docker escribe sus propias reglas de firewall.
- Los contenedores no se inician de nuevo después de un reinicio, a menos que lo haya configurado previamente.
- Las imágenes, los contenedores, los volúmenes y la caché de compilación crecen hasta llenar el disco.
Cada sección siguiente identifica el fallo, muestra la cadena que verá realmente y enlaza con la guía que lo corrige en detalle. Si todavía no ha escrito un archivo compose, lea primero Conceptos básicos de Docker Compose en un VPS y vuelva después. Esta página presupone que ya sabe iniciar una pila.
¿Cuánta RAM utiliza un contenedor de Docker?
Menos de lo que espera la mayoría. Un contenedor es un proceso dentro de un cgroup (grupo de control), no una máquina virtual. Por tanto, no tiene un kernel invitado ni una asignación fija. El consumo corresponde a lo que utiliza el proceso interno. Por eso, una pila completa cabe en 2 GB cuando la misma pila creada con máquinas virtuales no cabría.
Las cifras siguientes son valores de inactividad habituales para imágenes estándar en Ubuntu 24.04 con la configuración predeterminada. Se obtuvieron de docker stats unos minutos después del arranque. Sirven como punto de partida para la planificación, no como referencia del rendimiento de su carga de trabajo. Ejecute docker stats --no-stream en su propio equipo antes de confiar en cualquier cifra, incluidas estas.
The data behind this chart
[
{
"label": "nginx",
"idle_mb": 8,
"budget_mb": 64
},
{
"label": "Redis 7",
"idle_mb": 12,
"budget_mb": 128
},
{
"label": "Traefik v3",
"idle_mb": 40,
"budget_mb": 128
},
{
"label": "PostgreSQL 16",
"idle_mb": 45,
"budget_mb": 512
},
{
"label": "Uptime Kuma",
"idle_mb": 95,
"budget_mb": 256
},
{
"label": "MariaDB 11",
"idle_mb": 190,
"budget_mb": 512
},
{
"label": "Nextcloud (Apache)",
"idle_mb": 210,
"budget_mb": 768
}
]Las dos columnas tienen funciones distintas. idle_mb indica lo que utiliza el contenedor cuando no hace nada. budget_mb indica cuánto debe reservar al planificar, porque el uso real no corresponde al estado de inactividad. PostgreSQL permanece cerca de 45 MB en inactividad y necesita 512 MB cuando las conexiones, las ordenaciones y la caché están activas. Planifique con la columna de presupuesto. Depure con la columna de inactividad.
Observe el patrón de esas 7 filas. nginx permanece en 8 MB en inactividad y Nextcloud en 210 MB. El proxy situado delante de las aplicaciones consume muy poca memoria. El tamaño del sistema debe definirse según la base de datos y la aplicación PHP.
Tenga en cuenta una advertencia sobre docker stats: la cifra de memoria incluye la caché de páginas que las lecturas de archivos del propio contenedor han cargado. Por eso aumenta durante un tiempo después del arranque y luego se estabiliza. Supervísela durante una hora antes de determinar que existe una fuga de memoria.
Dimensionamiento de un VPS: qué cabe en 2 GB, 4 GB y 8 GB
Descuente primero la parte que necesita el host. El kernel, systemd, journald, sshd y el daemon de Docker usan la misma RAM que los contenedores, y dockerd con containerd ocupa alrededor de 100 MB. También necesita memoria libre para la caché de páginas y para el pico de consumo que se produce cuando se crea una imagen o se ejecuta un volcado de la base de datos.
The data behind this chart
[
{
"plan": "2 GB VPS",
"total_mb": 2048,
"host_reserve_mb": 768,
"container_mb": 1280
},
{
"plan": "4 GB VPS",
"total_mb": 4096,
"host_reserve_mb": 1024,
"container_mb": 3072
},
{
"plan": "8 GB VPS",
"total_mb": 8192,
"host_reserve_mb": 1536,
"container_mb": 6656
}
]host_reserve_mb cubre el sistema operativo, el daemon de Docker y el margen que mantiene el servidor operativo bajo carga. Lo que queda es container_mb, y ese es el único valor que puede asignar. La reserva aumenta con el plan, desde 768 MB en el servidor más pequeño hasta 1536 MB en el más grande, porque un servidor más grande ejecuta más contenedores, escribe más registros y necesita más caché de páginas.
Un plan de 2 GB deja 1280 MB para los contenedores. Asigne 512 MB a PostgreSQL y 128 MB a Traefik, y ya habrá consumido la mitad. El resto permite ejecutar dos aplicaciones pequeñas de unos 256 MB cada una. Es un servidor real y útil. No hay espacio suficiente para añadir Nextcloud y un clúster de búsqueda.
Un plan de 4 GB deja 3072 MB. En ese espacio caben simultáneamente una base de datos, un proxy inverso, tres aplicaciones y un contenedor de monitorización. Es el tamaño mínimo recomendable para cualquier servicio importante, porque la memoria disponible absorbe el impacto de una implementación defectuosa.
Un plan de 8 GB deja 6656 MB de sus 8192 MB, y el límite suele pasar de la memoria a la CPU o al rendimiento del disco. Si los cálculos indican que su pila no cabe, compre el plan más grande en lugar de intentar ajustarla hasta el límite: cuánto cuesta realmente un VPS explica cuánto valen al mes los gigabytes adicionales.
Dos reglas mantienen los cálculos ajustados a la realidad. Establezca un límite de memoria para cada servicio, para que un proceso descontrolado no consuma toda la memoria del servidor. Y no asigne todo el presupuesto, porque docker compose build y pg_dump necesitan memoria precisamente en el peor momento. Límites de memoria en Docker Compose muestra la sintaxis y los errores habituales.
¿Por qué mi contenedor termina con el código 137?
Porque el kernel lo terminó. 137 es 128 más 9, y la señal 9 es SIGKILL. El contenedor solicitó más memoria de la permitida y el OOM killer lo terminó.
CONTAINER ID IMAGE STATUS
9f2c1a4d7b3e postgres:16 Exited (137) 4 minutes agoConfirme la causa en lugar de hacer suposiciones:
docker inspect my-db | grep -i oomkilled
sudo journalctl -k | grep -i "out of memory""OOMKilled": true significa que el contenedor alcanzó su propio límite de cgroup, y el registro del kernel indica el proceso que seleccionó:
Memory cgroup out of memory: Killed process 2417 (postgres) total-vm:1284416kBEste es el caso favorable, porque el daño se limitó a un contenedor. El caso desfavorable es un contenedor sin ningún límite. Sin un límite, su techo es toda la máquina. Por tanto, una fuga de memoria en un servicio agota los recursos del host y el kernel selecciona una víctima por tamaño en todo el sistema. La línea del registro pierde el prefijo Memory cgroup y muestra Out of memory: Killed process 2417 (postgres). El proceso seleccionado suele ser la base de datos, mientras el contenedor que causó la fuga sigue ejecutándose. Por eso es más importante establecer un límite para cada servicio que acertar con el valor exacto de un límite concreto.
La swap cambia el momento del fallo, no el cálculo. La mayoría de las imágenes de VPS se distribuyen sin swap. Compruébelo con swapon --show, que no muestra nada cuando no existe swap. Un archivo de swap proporciona al kernel un lugar donde colocar las páginas inactivas y le da unos minutos para detectar el problema.
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -hfree -h ahora debería mostrar un total distinto de cero en la fila Swap. La swap no añade RAM. Un equipo sometido a presión constante de memoria se ralentiza hasta tal punto que no podrá conectarse por SSH para corregirlo. Por tanto, use la swap como margen de seguridad y corrija el dimensionamiento.
¿Por qué UFW no bloquea el puerto publicado de Docker?
Porque el tráfico nunca llega a la cadena que protege UFW. Cuando publica un puerto con -p 5432:5432 o con una entrada ports: de Compose, el daemon escribe una regla DNAT (traducción de direcciones de red de destino) en la tabla nat y una regla de aceptación en su propia cadena DOCKER. Un paquete dirigido a un contenedor se reenvía a ese contenedor en lugar de entregarse al host, por lo que se procesa en la ruta FORWARD y nunca pasa por las reglas INPUT que escribe UFW.
Puede observar este comportamiento en el servidor:
sudo ufw status | grep 5432
sudo iptables -t nat -L DOCKER -n | grep 5432UFW puede mostrar 5432 DENY IN Anywhere mientras la tabla nat contiene una regla DNAT tcp ... to:172.18.0.2:5432 para el mismo puerto. Desde otro equipo, nc -vz your.server.ip 5432 todavía se conecta. La base de datos está expuesta a Internet y el firewall indica que no lo está.
La solución es publicar menos puertos. Los contenedores de un mismo proyecto de Compose comparten una red y se alcanzan mediante el nombre del servicio, por lo que una base de datos que sólo presta servicio a la aplicación contigua no necesita ninguna entrada ports:. Si necesita acceso local, vincule la publicación a loopback:
services:
db:
image: postgres:16
ports:
- "127.0.0.1:5432:5432"Después de docker compose up -d, nc -vz your.server.ip 5432 desde fuera falla, mientras que psql -h 127.0.0.1 -p 5432 en el equipo sigue funcionando. En una pila pequeña correctamente configurada, sólo el reverse proxy publica puertos: 80 y 443. Por qué los puertos publicados de Docker evitan UFW explica la cadena DOCKER-USER para los casos en los que debe publicar un puerto y filtrarlo igualmente. Conceptos básicos del firewall UFW explica las reglas del host subyacentes.
¿Por qué han desaparecido mis contenedores después de reiniciar?
Porque nada les indicó que debían volver a iniciarse. Un contenedor se crea con la política de reinicio no si no se especifica ninguna. Por eso, después de reiniciar, queda detenido y el daemon no interviene. Los reinicios no son infrecuentes en un VPS: las actualizaciones del kernel mediante actualizaciones desatendidas, el mantenimiento del proveedor y la secuencia OOM anterior pueden provocar cualquiera de ellos.
Deben cumplirse dos condiciones. El daemon debe iniciarse durante el arranque:
systemctl is-enabled dockerEn una instalación estándar de Ubuntu, muestra enabled. Después, cada servicio necesita una política:
services:
app:
image: ghcr.io/example/app:1.4
restart: unless-stoppedunless-stopped vuelve a iniciar el contenedor después de un reinicio y respeta los contenedores que detuvo intencionadamente. always también reinicia los contenedores que detuvo deliberadamente cada vez que se reinicia el daemon, lo que puede resultar inesperado durante la depuración. Editar el archivo no basta, porque la política de reinicio se establece cuando se crea el contenedor. Ejecute docker compose up -d para recrearlo y, después, compruebe el valor activo:
docker inspect my-app | grep -A3 RestartPolicyDespués, reinicie el equipo de forma intencionada y ejecute docker compose ps en el directorio del proyecto. Si una pila sobrevive a un reinicio planificado, también sobrevivirá a uno imprevisto. Si la pila necesita garantizar un orden de inicio o ejecutar un trabajo único durante el arranque, una unidad de systemd es una opción más adecuada: iniciar Docker Compose durante el arranque contiene el archivo de unidad. Para comprobar si un contenedor que ha vuelto a iniciarse realmente está atendiendo peticiones, añada comprobaciones de estado de Compose.
¿Por qué está lleno el disco de mi VPS?
Porque Docker conserva todo hasta que se le indica que no lo haga. Cada etiqueta de imagen que haya descargado, cada contenedor detenido, cada volumen anónimo que haya quedado tras recrear un contenedor y cada capa de la caché de compilación permanecen en el disco. En un sistema de archivos raíz de 40 GB u 80 GB, algo normal en estos tamaños de plan, esto provoca una interrupción del servicio en meses, no en años.
Un disco lleno no se parece a un bloqueo del sistema. Recibe no space left on device de un contenedor, de apt, de journald y de docker pull durante la misma hora. PostgreSQL deja de aceptar escrituras. El servidor sigue activo, por lo que el problema es más difícil de detectar que un bucle de reinicios.
Compruebe el estado antes de eliminar nada:
docker system df
df -h /docker system df divide el total entre imágenes, contenedores, volúmenes locales y caché de compilación, con una columna RECLAIMABLE junto a cada categoría. En un servidor que compila sus propias imágenes, la caché de compilación suele ser la categoría más grande.
docker image prune -a
docker builder prune
docker system dfdocker image prune -a elimina todas las imágenes que ningún contenedor está utilizando. docker builder prune borra la caché de compilación. Ambas operaciones son seguras mientras los servicios están en ejecución, porque se omite todo lo que está en uso. En cambio, docker system prune --volumes no es seguro: elimina todos los volúmenes a los que ningún contenedor hace referencia en ese momento. Una pila que haya detenido durante el fin de semana tiene exactamente esa situación, y su volumen de base de datos también se elimina. Lea montajes bind frente a volúmenes con nombre antes de escribir esa opción y haga primero una copia de seguridad.
Los registros de los contenedores son una fuente de crecimiento menos evidente. El controlador predeterminado json-file no tiene límite de tamaño, por lo que un contenedor que genere muchos mensajes puede escribir gigabytes en /var/lib/docker/containers. Establezca un límite para todos los contenedores en /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}Aplique el cambio con sudo systemctl restart docker. Esta operación reinicia los contenedores, así que elija el momento adecuado. El límite se aplica a los contenedores creados después del cambio. Por tanto, recree los contenedores en ejecución con docker compose up -d --force-recreate y confirme el resultado:
docker inspect my-app | grep -A5 LogConfig
sudo du -sh /var/lib/docker/containersLa salida de inspect debe mostrar max-size configurado. Si está vacío, ese contenedor se creó antes del cambio y sigue escribiendo sin límite.
Hábitos para mantener sano un servidor Docker pequeño
Nada de esto requiere un panel ni una herramienta que tenga que aprender.
- Ejecute
docker system dfydf -h /el primer día de cada mes. Son dos comandos y treinta segundos. Así verá la tendencia mucho antes de que se convierta en una interrupción del servicio. - Asigne un límite de memoria a cada servicio, incluidos los que parecen pequeños. El límite convierte una interrupción de todo el host en un único contenedor reiniciado.
- Supervise el servidor desde otro lugar para recibir avisos sobre la presión de memoria o de disco antes de que actúe el kernel. Uptime Kuma se ejecuta en un contenedor y consume aproximadamente 95 MB en reposo.
- Haga copias de seguridad de los volúmenes, no de los contenedores. El contenedor es desechable, pero el volumen no. copias de seguridad de restic en un VPS cubre una programación y una prueba de restauración.
- Fije las etiquetas de imagen en el archivo compose y actualícelas el día que elija. Con
latest, la versión que obtiene del siguientedocker compose pulles la que se publicó esa mañana.
Un VPS pequeño que ejecuta Docker se mantiene estable durante años si cuatro valores permanecen dentro de los límites: el presupuesto de memoria, la lista de puertos publicados, la política de reinicio de cada servicio y el espacio libre en disco. Todo lo demás es el mismo Docker que ya ejecuta en casa.
FAQ
¿Cuánta RAM necesito para ejecutar Docker en un VPS?
Docker consume pocos recursos. El daemon y containerd juntos ocupan cerca de 100 MB; el resto depende de los contenedores. Reserve primero la parte del host: 768 MB en un servidor de 2048 MB para el sistema operativo, el daemon y un margen de seguridad. Quedan 1280 MB para los contenedores. Una base de datos de 512 MB, un proxy inverso de 128 MB y dos aplicaciones pequeñas caben en ese límite. Mida su propia pila con docker stats --no-stream en lugar de confiar en una cifra publicada.
¿Puedo ejecutar Docker en un VPS de 1 GB?
Sí, para uno o dos contenedores ligeros. Cree un archivo de swap antes de empezar. Aproximadamente la mitad de un servidor de 1 GB se consume cuando el sistema operativo y el daemon de Docker están en ejecución. Queda espacio para una aplicación pequeña y un proxy inverso, pero no para una base de datos con carga real. La creación de imágenes en un servidor de ese tamaño puede fallar o dejar sin recursos a otro proceso. Cree las imágenes en otro equipo y descargue la imagen terminada.
¿UFW protege un contenedor de Docker?
No en los puertos publicados. Docker escribe sus propias reglas DNAT y de reenvío. Por eso, un paquete dirigido a un puerto publicado del contenedor se reenvía al contenedor en lugar de entregarse al host. Las reglas INPUT administradas por UFW nunca lo ven. ufw deny 5432 puede estar activo mientras ese puerto responde desde Internet. Publique el puerto en loopback con 127.0.0.1:5432:5432, no publique los servicios internos o filtre en la cadena DOCKER-USER.
¿Se reiniciarán mis contenedores después de reiniciar un VPS?
Sólo si se crearon con una política de reinicio. Establezca restart: unless-stopped en cada servicio y ejecute docker compose up -d para recrear los contenedores con esa configuración. Confirme que systemctl is-enabled docker muestre enabled. Después, reinicie el servidor de forma intencionada y compruebe docker compose ps. Una política de reinicio que nunca se ha probado no es una política de reinicio fiable.
¿Con qué frecuencia debo eliminar imágenes de Docker?
Una vez al mes es suficiente para la mayoría de los servidores pequeños. También puede hacerlo cuando docker system df informe de espacio recuperable que necesite. docker image prune -a y docker builder prune son seguros mientras los servicios están en ejecución, porque omiten las imágenes y la caché que están en uso. Evite docker system prune --volumes salvo que sepa exactamente qué volúmenes no tienen referencias. Ese comando elimina los datos de cualquier pila que esté detenida.