Instalar Docker en un VPS Ubuntu 24.04/22.04: método oficial
- 3 de octubre de 2026
- 09:00
- Por Adrien Roche
- Tutoriales

Antes de empezar: un VPS KVM, una versión de Ubuntu soportada y un usuario con sudo
Docker Engine no trae su propio kernel. Todos los contenedores comparten el kernel del host y dependen de los namespaces de Linux, los cgroups y netfilter, así que el daemon necesita un VPS donde el kernel sea tuyo. Eso es justo lo que ofrece un VPS KVM (virtualización completa). En los planes basados en contenedores construidos sobre OpenVZ o LXC, el kernel pertenece al proveedor y Docker o bien se niega a arrancar, o bien solo funciona si el host ha habilitado el anidamiento. Compruébalo antes de instalar nada:
systemd-detect-virt # expect: kvm
. /etc/os-release && echo "$VERSION_CODENAME" # noble (24.04) or jammy (22.04)
uname -m # x86_64 or aarch64
Docker publica paquetes para Ubuntu 24.04 LTS (noble) y 22.04 LTS (jammy) de 64 bits, en x86_64 y arm64. También necesitas un usuario con permisos sudo; las secciones sobre el grupo docker y el modo rootless dan por hecho una cuenta normal en lugar de root.
Instalar Docker Engine desde el repositorio apt oficial
Hay cuatro formas habituales de instalar Docker en Ubuntu, y solo una es el método que Docker documenta y soporta para servidores:
| Método | Paquete | Veredicto |
|---|---|---|
| Repositorio apt de Docker | docker-ce desde download.docker.com | Recomendado: versiones actuales, actualizaciones vía apt upgrade, plugins Compose y Buildx incluidos. |
| Universe de Ubuntu | docker.io | Funciona, pero la compilación propia de Ubuntu va por detrás de upstream y no incluye los plugins. |
| Snap | snap install docker | Instalación confinada con sus propias rutas; fuente frecuente de sorpresas de tipo «permission denied». Evítalo en servidores. |
| Script de conveniencia | get.docker.com | Los mismos paquetes, pero canalizados sin revisión a una shell de root. Solo para máquinas desechables. |
Paso 1: eliminar los paquetes en conflicto
Si la imagen del VPS ya incluye el docker.io de Ubuntu, un docker-compose antiguo o podman-docker, elimínalos para que las dos compilaciones no se peleen por /usr/bin/docker. Este bucle procede de la documentación de Docker y es inofensivo en un sistema limpio; deja intacto /var/lib/docker:
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do
sudo apt-get remove $pkg
done
Paso 2: añadir la clave GPG y la fuente apt de Docker
La clave va en /etc/apt/keyrings/ y se referencia con signed-by=, lo que la limita a este único repositorio en lugar de confiar en ella globalmente mediante el obsoleto apt-key. El nombre en clave se lee de /etc/os-release, así que el mismo bloque funciona en 24.04 y en 22.04:
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
Un error de firma en ese último apt-get update casi siempre significa que el archivo de la clave no es legible; vuelve a ejecutar la línea del chmod.
Paso 3: instalar el motor y sus plugins
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
docker-ce es el daemon, docker-ce-cli el cliente, containerd.io el runtime que hay debajo, y los dos plugins aportan docker buildx y docker compose. En Ubuntu, el script de post-instalación del paquete arranca docker.service de inmediato y lo habilita en el arranque.
Paso 4: verificar con hello-world
sudo docker run hello-world
docker --version
sudo docker info | head -n 20
hello-world es la prueba definitiva: el cliente habla con el daemon, el daemon descarga una imagen diminuta de Docker Hub, crea un contenedor, lo ejecuta y devuelve su salida. Si ves «Hello from Docker!», todas las capas funcionan. En una instalación sana de 24.04 o 22.04, docker info muestra Storage Driver: overlay2 y Cgroup Version: 2.
Usar Docker sin sudo: el grupo docker
El daemon escucha en el socket Unix /var/run/docker.sock, propiedad de root:docker, de modo que los miembros del grupo docker pueden usar la CLI sin sudo:
sudo groupadd docker # usually exists already; harmless if it does
sudo usermod -aG docker $USER
newgrp docker # or log out and back in
docker run hello-world
Entiende lo que acabas de conceder. Cualquiera que pueda acceder a ese socket puede ejecutar docker run -v /:/host --privileged ... y leer o reescribir cualquier archivo del VPS, lo que hace que pertenecer al grupo docker sea equivalente a root, sin petición de contraseña y sin registro en el log de sudo. Trátalo exactamente igual que el acceso sudo; nuestra guía sobre cambiar de usuario y la escalada de privilegios en Ubuntu explica por qué esa distinción importa en una máquina multiusuario.
El plugin Docker Compose
El paquete docker-compose-plugin proporciona Compose v2 como el subcomando docker compose (con espacio). El antiguo binario en Python docker-compose está descontinuado y no debe instalarse junto a él. Comprueba la versión y prueba después con un compose.yaml mínimo:
docker compose version
mkdir -p ~/web && cd ~/web
cat > compose.yaml <<'EOF'
services:
web:
image: nginx:alpine
ports:
- "127.0.0.1:8080:80"
restart: unless-stopped
EOF
docker compose up -d
docker compose ps
curl -I http://127.0.0.1:8080
docker compose down
Dos detalles son deliberados: restart: unless-stopped vuelve a levantar el contenedor tras un reinicio, y el puerto se enlaza a 127.0.0.1 en lugar de a todas las interfaces, por los motivos que explica la sección sobre el firewall.
Modo rootless: un daemon que no es root
El modo rootless ejecuta el daemon y los contenedores dentro de un namespace de usuario, de forma que una fuga del contenedor aterriza en una cuenta sin privilegios en lugar de en root. Es la opción correcta cuando varias personas comparten un VPS. Los requisitos son los rangos subordinados de UID/GID (Ubuntu los crea para los usuarios normales en /etc/subuid y /etc/subgid), las herramientas uidmap, una sesión D-Bus de usuario y los extras rootless de Docker:
sudo apt-get install -y uidmap dbus-user-session docker-ce-rootless-extras
grep "^$USER:" /etc/subuid /etc/subgid # each should show a range of 65536 IDs
# optional: stop the system-wide daemon if you only want rootless
sudo systemctl disable --now docker.service docker.socket
dockerd-rootless-setuptool.sh install
La herramienta de configuración crea una unidad systemd por usuario e imprime las dos variables que necesita tu shell. Añádelas a ~/.bashrc y luego haz que el daemon de usuario arranque en el inicio sin necesidad de iniciar sesión:
export PATH=/usr/bin:$PATH
export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock
systemctl --user enable docker
sudo loginctl enable-linger $(whoami)
El modo rootless tiene límites reales: no se pueden publicar puertos por debajo de 1024 salvo que ejecutes sudo setcap cap_net_bind_service=ep $(which rootlesskit) y reinicies el daemon de usuario, las redes overlay no están disponibles y los datos viven en ~/.local/share/docker. En Ubuntu 24.04, AppArmor restringe los namespaces de usuario sin privilegios; los paquetes deb incluyen el perfil que necesita rootlesskit, pero una instalación con binario estático exige añadirlo a mano tal y como describe la documentación de rootless.
Comportamiento en el arranque, almacenamiento y rotación de logs
Dos cosas distintas deciden si tus contenedores están en marcha tras un reinicio: el daemon debe estar habilitado y cada contenedor debe llevar una política de reinicio. Los paquetes de Ubuntu habilitan el servicio por ti; confírmalo y, si los estados de la unidad te resultan poco familiares, nuestra referencia sobre listar servicios con systemctl cubre todas las columnas:
systemctl is-enabled docker.service containerd.service
sudo systemctl enable docker.service containerd.service # only if the line above said disabled
Los contenedores arrancados sin --restart se quedan apagados tras un reinicio; usa --restart unless-stopped en la línea de comandos o restart: unless-stopped en Compose para todo lo que sea permanente.
Todo lo que Docker almacena —capas de imagen, sistemas de archivos de contenedores, volúmenes, logs— vive bajo /var/lib/docker, y en un VPS pequeño este es el directorio que llena el disco. Vigílalo con docker system df, recupera espacio de las capas huérfanas con docker system prune y lee el aviso antes de añadir -a, que además borra todas las imágenes que no use un contenedor en ejecución. Si el VPS tiene un segundo disco, apunta data-root hacia él en /etc/docker/daemon.json antes de descargar nada grande.
Los logs merecen un aviso específico. El driver json-file por defecto escribe cada línea de la salida estándar del contenedor en /var/lib/docker/containers/<id>/<id>-json.log y no realiza ninguna rotación a menos que la configures. Un contenedor parlanchín puede devorar el disco entero en una semana. Fija límites a nivel de daemon desde el primer día:
sudo tee /etc/docker/daemon.json > /dev/null <<'EOF'
{
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "3" },
"live-restore": true
}
EOF
sudo systemctl restart docker
Las opciones de log se aplican a los contenedores creados después del reinicio del daemon, así que recrea una vez los que lleven mucho tiempo en marcha. live-restore permite reiniciar el daemon para actualizarlo sin matar los contenedores en ejecución.
Docker y ufw: los puertos publicados se saltan tu firewall
Esta es la parte que pilla a casi todo el mundo en un VPS público. Docker gestiona sus propias reglas de iptables: cuando publicas un puerto con -p 8080:80, el daemon inserta una regla DNAT en la cadena PREROUTING de la tabla nat y una regla de aceptación en FORWARD. Las reglas de ufw viven en INPUT, que el tráfico reenviado nunca atraviesa. Por eso ufw deny 8080 no tiene ningún efecto, y un contenedor de base de datos publicado en 0.0.0.0:5432 es accesible desde todo internet mientras ufw informa de que el puerto está bloqueado. Busca 0.0.0.0: en la columna PORTS de docker ps, o ejecuta ss -tlnp; nuestra guía sobre comprobar los puertos abiertos en Linux muestra cómo leer esa salida y confirmar la exposición desde fuera.
Dos soluciones limpias, por orden de preferencia:
- Enlaza los puertos publicados al loopback.
-p 127.0.0.1:8080:80mantiene el servicio accesible solo desde el propio VPS, normalmente detrás de un proxy inverso como nginx o Caddy que expones de forma deliberada. Los contenedores que solo hablan entre sí no necesitan ningún-p. - Filtra en la cadena
DOCKER-USER. Docker evalúa esta cadena antes que sus propias reglas deFORWARDy nunca la reescribe. Como el DNAT ya ha ocurrido cuando un paquete llega ahí, empareja el puerto original con conntrack:
sudo iptables -I DOCKER-USER -i eth0 ! -s 203.0.113.10 -p tcp \
-m conntrack --ctorigdstport 8080 --ctdir ORIGINAL -j DROP
Sustituye eth0 por tu interfaz pública y 203.0.113.10 por la dirección autorizada. Estas reglas no son persistentes; guárdalas con el paquete iptables-persistent. Poner "iptables": false en daemon.json rompe la red de los contenedores de formas difíciles de depurar; no lo toques.
Desinstalar Docker por completo
Primero purga los paquetes y después borra los directorios de datos, que apt deja en su sitio a propósito:
sudo apt-get purge -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras
sudo rm -rf /var/lib/docker /var/lib/containerd
sudo rm -f /etc/apt/sources.list.d/docker.list /etc/apt/keyrings/docker.asc
sudo apt-get autoremove -y
Para una instalación rootless, ejecuta antes dockerd-rootless-setuptool.sh uninstall como el usuario y borra después ~/.local/share/docker.
Todo esto da por hecho un servidor donde tienes root, controlas el kernel y puedes reescribir iptables sin pedir permiso a nadie. Si necesitas esa máquina, el VPS Linux KVM con acceso root completo de rdp.monster viene con Ubuntu 24.04 o 22.04, CPU y RAM dedicadas, ancho de banda ilimitado (uso razonable), sin KYC, con criptomonedas aceptadas, desde 8,99 $/mes, y se entrega unos 10 segundos después de la confirmación del pago, así que puedes estar ejecutando la comprobación hello-world de arriba en tu propio servidor justo después de la compra.
Preguntas frecuentes
¿Qué versión de Docker instala apt en Ubuntu y cómo la actualizo?
apt-get install docker-ce descarga la versión estable más reciente disponible para el nombre en clave de tu Ubuntu, y cada sudo apt-get update && sudo apt-get upgrade posterior te hace avanzar. Ejecuta docker version para ver las versiones del cliente y del servidor. Para fijar una versión concreta, lista las candidatas con apt-cache madison docker-ce e instala docker-ce=<version> junto con el docker-ce-cli correspondiente, y luego aplica apt-mark hold a ambos paquetes.¿Por qué recibo «permission denied while trying to connect to the Docker daemon socket»?
root:docker, así que un usuario normal recibe permission denied. O bien antepones sudo a los comandos, o bien te añades con sudo usermod -aG docker $USER y abres una sesión nueva (o ejecutas newgrp docker), porque los cambios de grupo solo se aplican a las sesiones nuevas. Si el error persiste, comprueba groups para confirmar la pertenencia, verifica que el daemon está en marcha con systemctl status docker y asegúrate de que DOCKER_HOST no apunta a un socket rootless que no existe.¿Debo instalar Docker con snap o con apt en Ubuntu Server?
daemon.json y los datos, y su ritmo de actualización lo controla snapd en lugar de ti. Esas rarezas aparecen como misteriosos permission denied y fallos de bind-mount. Los paquetes apt siguen las convenciones estándar de Ubuntu, se integran con systemd con normalidad y reciben las actualizaciones upstream mediante el mismo apt upgrade que ya ejecutas.¿Cuánta RAM y disco necesita un VPS con Docker?
/var/lib/docker, y 20 GB se llenan rápido sin docker system prune ni rotación de logs. Prefiere CPU y RAM dedicadas antes que recursos compartidos con ráfagas, porque las compilaciones dan picos fuertes y los contenedores no gestionan bien el swap.Adrien Roche, Editor de infraestructura y hosting
Ingeniero de sistemas con más de 10 años operando flotas de Windows Server y Linux. Adrien mantiene la documentación de infraestructura de rdp.monster y escribe nuestras guías sobre RDP, hosting VPS, administración de servidores, redes y herramientas de privacidad.




