Qué cambia al usar Docker en un VPS
Docker en un VPS usa el mismo motor, pero con menos margen: la RAM se agota, los puertos publicados omiten UFW, los contenedores no vuelven solos y el disco se llena.
Qué cambia al ejecutar Docker en un VPS
Docker en un VPS utiliza el mismo motor y las mismas imágenes que Docker en el equipo portátil, por lo que todos los comandos que ya conoce siguen funcionando. Lo que cambia es el margen disponible. Un equipo portátil tiene memoria libre, un firewall que nadie está escaneando y un disco lo bastante grande como para no tener que revisarlo. Un servidor alquilado tiene un límite de memoria fijo, una dirección IP pública que recibe escaneos a los pocos minutos de iniciar 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 vuelven a iniciarse después de un reinicio a menos que se haya configurado de antemano.
- 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, la cadena que verá realmente y la guía que lo corrige en profundidad. Si todavía no ha escrito un archivo de compose, lea primero Conceptos básicos de Docker Compose en un VPS y vuelva después. Esta página supone que ya puede iniciar un stack.
¿Cuánta RAM usa 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 depende de lo que use el proceso que se ejecuta dentro. Por eso, una pila completa cabe en 2 GB cuando la misma pila basada en máquinas virtuales no cabría.
Las cifras siguientes son valores de inactividad habituales para imágenes sin modificar en Ubuntu 24.04 con la configuración predeterminada. Se han obtenido de docker stats unos minutos después del arranque. Sirven como punto de partida para planificar, no como referencia de rendimiento para 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 usa el contenedor cuando no hace nada. budget_mb indica lo que debe reservar al planificar, porque el uso real no corresponde al estado de inactividad. PostgreSQL usa cerca de 45 MB en inactividad y necesita 512 MB cuando las conexiones, las operaciones de ordenación y la caché están activas. Planifique con la columna de reserva. Depure con la columna de inactividad.
Observe la distribución de esas 7 filas. nginx usa 8 MB en inactividad y Nextcloud 210 MB. El proxy situado delante de las aplicaciones apenas consume memoria. El tamaño del servidor debe determinarse principalmente por la base de datos y la aplicación PHP.
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. Monitorícela durante una hora antes de concluir que existe una fuga de memoria.
Dimensionar 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 ocupan 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 equipo operativo bajo carga. Lo que queda es container_mb, y esa es la única cantidad que puede asignar. La reserva aumenta con el plan, desde 768 MB en el equipo más pequeño hasta 1536 MB en el más grande, porque un equipo 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. Esa cantidad permite ejecutar simultáneamente una base de datos, un reverse proxy, tres aplicaciones y un contenedor de monitorización. Es el tamaño mínimo que merece la pena usar para algo importante, porque la memoria disponible absorbe el impacto de un despliegue defectuoso.
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. Algunos contenedores calculan su tamaño a partir de su configuración, no de la carga: un servidor de modelos local reserva la caché KV en proporción a su ventana de contexto, por lo que aumentar num_ctx de Ollama puede añadir gigabytes al presupuesto antes de que llegue una sola petición. Si los cálculos indican que la pila no cabe, compre el plan más grande en lugar de intentar ajustarla: cuánto cuesta realmente un VPS explica cuánto valen al mes esos gigabytes adicionales.
Dos reglas mantienen los cálculos bajo control. Establezca un límite de memoria para cada servicio, de modo que un proceso descontrolado no consuma toda la memoria del equipo. Además, no asigne todo el presupuesto, porque docker compose build y pg_dump también necesitan memoria en el peor momento. Límites de memoria en Docker Compose explica la sintaxis y los problemas 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 killer de falta de memoria (OOM) 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 indica que el contenedor alcanzó su propio límite de cgroup, y el registro del kernel identifica el proceso que seleccionó:
Memory cgroup out of memory: Killed process 2417 (postgres) total-vm:1284416kBEse es el caso favorable, porque el impacto quedó limitado a un contenedor. El caso problemático 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 consume los recursos del host y el kernel selecciona después una víctima según su 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 tiene 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. Un archivo de swap proporciona al kernel un lugar donde colocar páginas inactivas. Esto 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 debería mostrar ahora 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 vuelve tan lento que no podrá conectarse por SSH para solucionarlo. Por tanto, trate la swap como un margen de seguridad y corrija el dimensionamiento.
¿Por qué UFW no bloquea mi puerto publicado por Docker?
Porque el tráfico nunca llega a la cadena que controla 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 observarlo 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 otra máquina, nc -vz your.server.ip 5432 sigue conectándose. La base de datos está en Internet público 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 comunican mediante el nombre del servicio, por lo que una base de datos que sólo presta servicio a la aplicación que la acompaña 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 falla desde el exterior y psql -h 127.0.0.1 -p 5432 sigue funcionando en el host. En una pila pequeña correctamente configurada, sólo el reverse proxy publica puertos: 80 y 443. Por qué los puertos publicados por 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 volvieran a iniciarse. Un contenedor se crea con la política de reinicio no si no se especifica otra. Por eso, tras un reinicio, queda detenido y el daemon no interviene. Los reinicios no son poco frecuentes en un VPS: las actualizaciones del kernel mediante actualizaciones desatendidas, el mantenimiento del proveedor y la secuencia OOM anterior pueden provocarlos.
Deben cumplirse dos condiciones. El daemon debe iniciarse durante el arranque:
systemctl is-enabled dockerEn una instalación estándar de Ubuntu, esto 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 haya detenido intencionadamente. always también reinicia los contenedores que haya detenido deliberadamente cada vez que se reinicia el daemon. Esto puede ser 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 compruebe después el valor activo:
docker inspect my-app | grep -A3 RestartPolicyA continuación, reinicie el servidor de forma intencionada y ejecute docker compose ps en el directorio del proyecto. Una pila que sobrevive a un reinicio planificado también sobrevive a uno imprevisto. Si la pila necesita garantizar un orden de inicio o ejecutar un trabajo de una sola vez durante el arranque, una unidad de systemd es una herramienta más adecuada: iniciar Docker Compose durante el arranque incluye el archivo de unidad. Para saber si un contenedor que ha vuelto a iniciarse realmente está atendiendo solicitudes, 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 lo contrario. 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 root de 40 GB u 80 GB, algo normal en planes de este tamaño, esto provoca una interrupción del servicio en meses y no en años.
Un disco lleno no parece un fallo del sistema. En la misma hora, recibe no space left on device de un contenedor, de apt, de journald y de docker pull. PostgreSQL deja de aceptar escrituras. El servidor sigue activo, por lo que es más difícil detectarlo que un bucle de reinicios.
Revise el estado antes de borrar:
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 no utiliza ningún contenedor. docker builder prune limpia la caché de compilación. Ambas operaciones son seguras mientras los servicios están en ejecución, porque omiten todo lo que está en uso. Lo que no es seguro es docker system prune --volumes, que 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 configuración, y su volumen de base de datos se elimina con ella. Lea montajes bind frente a volúmenes con nombre antes de escribir ese indicador y haga una copia de seguridad primero.
Los registros de los contenedores son el crecimiento menos visible. El controlador predeterminado json-file no tiene límite de tamaño, por lo que un contenedor que genera muchos mensajes escribe 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"
}
}Aplíquelo 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 que están 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 es anterior al 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 provoque 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 que se reinicia.
- Supervise el servidor desde otro sistema para detectar la presión de memoria o de disco antes de que intervenga el kernel. Uptime Kuma se ejecuta en un contenedor y permanece inactivo con unos 95 MB.
- Haga copias de seguridad de los volúmenes, no de los contenedores. El contenedor es desechable y el volumen no. copias de seguridad de restic en un VPS incluye 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 obtenga del siguientedocker compose pullserá la que se haya publicado esa mañana.
Un VPS pequeño que ejecute Docker se mantiene sano durante años cuando 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 de los recursos depende de los contenedores. Reserve primero la parte del host: 768 MB en un equipo de 2048 MB para el sistema operativo, el daemon y un margen adicional. Esto deja 1280 MB disponibles para los contenedores. Una base de datos de 512 MB, un reverse proxy de 128 MB y dos aplicaciones pequeñas caben en ese presupuesto. 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í, con uno o dos contenedores ligeros. Cree un archivo de swap antes de empezar. Aproximadamente la mitad de un equipo de 1 GB se consume cuando el sistema operativo y el daemon de Docker están en ejecución. Esto deja espacio para una aplicación pequeña y un reverse proxy, pero no para una base de datos con carga real. La creación de imágenes en un equipo de ese tamaño fallará o provocará que otro proceso se quede sin recursos. Cree las imágenes en otro equipo y descargue la imagen terminada.
¿UFW protege un contenedor de Docker?
No en el caso de 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, y nunca llega a las reglas INPUT que administra UFW. ufw deny 5432 puede estar activo mientras ese puerto responde desde Internet. Publique el puerto en loopback con 127.0.0.1:5432:5432, mantenga los servicios internos sin publicar o filtre en la cadena DOCKER-USER.
¿Se reiniciarán mis contenedores después de reiniciar el VPS?
Sólo si se crearon con una política de reinicio. Configure restart: unless-stopped en cada servicio, ejecute docker compose up -d para recrear los contenedores con esa configuración y confirme que systemctl is-enabled docker muestra enabled. Después, reinicie el sistema 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 las 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 cuya falta afectaría al sistema. 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, porque elimina los datos de cualquier pila que esté detenida.