Docker cambió cómo desplegamos aplicaciones. En lugar de configurar cada servidor manualmente, empaquetas tu app con todo lo que necesita y la ejecutas en cualquier lugar.
Para un VPS, Docker significa despliegues consistentes, aislamiento de aplicaciones y la posibilidad de correr múltiples servicios sin conflictos.
Esta guía te enseña Docker desde cero, orientado a uso en VPS.
Qué es Docker y por qué usarlo
El problema que resuelve
Sin Docker:
"Funciona en mi máquina" → No funciona en el servidor
- Diferentes versiones de PHP
- Librerías faltantes
- Configuraciones distintas
Con Docker:
"Funciona en mi máquina" → Funciona en todas partes
- El contenedor incluye todo
- Misma versión siempre
- Configuración idéntica
Beneficios en VPS
| Beneficio | Explicación |
|---|---|
| Aislamiento | Cada app en su contenedor, sin conflictos |
| Consistencia | Mismo entorno en desarrollo y producción |
| Portabilidad | Migrar a otro VPS es copiar contenedores |
| Escalabilidad | Levantar más instancias fácilmente |
| Limpieza | Borrar app = borrar contenedor, sin residuos |
Conceptos básicos
Imagen (Image)
Una imagen es una plantilla de solo lectura. Contiene el sistema operativo, la aplicación y todas sus dependencias.
Imagen = Receta
Ejemplo: nginx:latest, wordpress:6.4, mysql:8
Contenedor (Container)
Un contenedor es una instancia ejecutándose de una imagen. Puedes tener múltiples contenedores de la misma imagen.
Contenedor = Plato preparado con la receta
Dockerfile
Archivo de texto con instrucciones para crear una imagen personalizada.
FROM php:8.3-fpm
RUN apt-get update && apt-get install -y libzip-dev
RUN docker-php-ext-install zip pdo_mysql
COPY . /var/www/html
Docker Compose
Herramienta para definir y ejecutar aplicaciones multi-contenedor. Un archivo YAML describe todos los servicios.
services:
web:
image: nginx
db:
image: mysql:8
Volumen (Volume)
Almacenamiento persistente. Los datos del contenedor se pierden al borrarlo; los volúmenes persisten.
Red (Network)
Permite que los contenedores se comuniquen entre sí de forma aislada.
Instalar Docker en tu VPS
Ubuntu/Debian
# Actualizar e instalar dependencias
sudo apt update
sudo apt install -y ca-certificates curl gnupg
# Añadir clave GPG oficial
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Añadir repositorio
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Instalar Docker
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Verificar instalación
sudo docker run hello-world
Si ves el mensaje de bienvenida, Docker funciona.
Usar Docker sin sudo
sudo usermod -aG docker $USER
# Cierra sesión y vuelve a entrar
Comandos esenciales
Imágenes
# Descargar imagen
docker pull nginx
# Listar imágenes
docker images
# Eliminar imagen
docker rmi nginx
Contenedores
# Ejecutar contenedor
docker run nginx
# Ejecutar en segundo plano (-d = detached)
docker run -d nginx
# Ejecutar con nombre
docker run -d --name mi-nginx nginx
# Ejecutar con puerto mapeado
docker run -d -p 8080:80 nginx
# Accede en http://tu-ip:8080
# Listar contenedores activos
docker ps
# Listar todos (incluidos parados)
docker ps -a
# Parar contenedor
docker stop mi-nginx
# Iniciar contenedor parado
docker start mi-nginx
# Eliminar contenedor
docker rm mi-nginx
# Eliminar contenedor forzado (aunque esté corriendo)
docker rm -f mi-nginx
Logs y acceso
# Ver logs
docker logs mi-nginx
# Ver logs en tiempo real
docker logs -f mi-nginx
# Entrar al contenedor
docker exec -it mi-nginx bash
# Usa 'sh' si bash no está disponible
Limpieza
# Eliminar contenedores parados
docker container prune
# Eliminar imágenes sin usar
docker image prune
# Eliminar todo lo no usado (cuidado)
docker system prune -a
Workflow típico con Docker
1. Definir con docker-compose.yml
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html
restart: unless-stopped
2. Levantar servicios
docker compose up -d
3. Ver estado
docker compose ps
4. Ver logs
docker compose logs -f
5. Parar todo
docker compose down
Ejemplos prácticos
Ejemplo 1: Nginx simple
# docker-compose.yml
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./sitio:/usr/share/nginx/html:ro
restart: unless-stopped
mkdir sitio
echo "<h1>Hola desde Docker</h1>" > sitio/index.html
docker compose up -d
Visita http://tu-ip y verás tu página.
Ejemplo 2: WordPress completo
# docker-compose.yml
version: '3.8'
services:
wordpress:
image: wordpress:latest
ports:
- "80:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: secreto123
WORDPRESS_DB_NAME: wordpress
volumes:
- wordpress_data:/var/www/html
depends_on:
- db
restart: unless-stopped
db:
image: mysql:8
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: secreto123
MYSQL_ROOT_PASSWORD: rootsecreto123
volumes:
- db_data:/var/lib/mysql
restart: unless-stopped
volumes:
wordpress_data:
db_data:
docker compose up -d
# Espera 30 segundos y accede a http://tu-ip
WordPress funcionando con MySQL en minutos.
Ejemplo 3: Aplicación Node.js
Dockerfile:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
docker-compose.yml:
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
NODE_ENV: production
restart: unless-stopped
docker compose up -d --build
Volúmenes: datos persistentes
Por qué importan
Sin volúmenes, los datos se pierden al eliminar el contenedor:
docker run -d --name mi-mysql mysql:8
docker rm -f mi-mysql
# ¡Datos perdidos!
Con volúmenes, persisten:
docker run -d --name mi-mysql -v mysql_data:/var/lib/mysql mysql:8
docker rm -f mi-mysql
docker run -d --name mi-mysql-nuevo -v mysql_data:/var/lib/mysql mysql:8
# ¡Datos recuperados!
Tipos de volúmenes
Volumen nombrado (recomendado):
volumes:
- mysql_data:/var/lib/mysql
Bind mount (carpeta del host):
volumes:
- ./datos:/var/lib/mysql
Volumen anónimo (no recomendado):
volumes:
- /var/lib/mysql
Gestionar volúmenes
# Listar volúmenes
docker volume ls
# Inspeccionar volumen
docker volume inspect mysql_data
# Eliminar volumen
docker volume rm mysql_data
# Eliminar volúmenes sin usar
docker volume prune
Redes en Docker
Red por defecto
Docker Compose crea una red automática donde los servicios se ven por nombre:
services:
web:
image: nginx
db:
image: mysql
web puede conectar a db usando db como hostname.
Red personalizada
services:
web:
networks:
- frontend
- backend
db:
networks:
- backend
networks:
frontend:
backend:
db solo es accesible desde backend, no desde frontend.
Docker con proxy inverso
Para múltiples aplicaciones en el mismo VPS:
Traefik (recomendado)
# docker-compose.yml
version: '3.8'
services:
traefik:
image: traefik:v2.10
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
- "[email protected]"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- letsencrypt:/letsencrypt
restart: unless-stopped
volumes:
letsencrypt:
Luego cada app se registra automáticamente:
services:
mi-app:
image: mi-app
labels:
- "traefik.enable=true"
- "traefik.http.routers.miapp.rule=Host(`miapp.tudominio.com`)"
- "traefik.http.routers.miapp.tls.certresolver=letsencrypt"
Seguridad básica
No correr como root
FROM node:20-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
No exponer puertos innecesarios
Solo expón lo necesario:
services:
db:
image: mysql
# NO hagas esto:
# ports:
# - "3306:3306"
# La BD solo debe ser accesible internamente
Usar imágenes oficiales
Prefiere nginx:alpine sobre random-user/nginx.
Variables de entorno para secretos
services:
db:
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
Recursos y límites
Evita que un contenedor consuma todo el VPS:
services:
app:
image: mi-app
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
Backups de contenedores
Backup de volúmenes
# Crear backup
docker run --rm -v mysql_data:/data -v $(pwd):/backup alpine tar cvf /backup/mysql_backup.tar /data
# Restaurar
docker run --rm -v mysql_data:/data -v $(pwd):/backup alpine tar xvf /backup/mysql_backup.tar -C /
Backup de base de datos
docker exec mi-mysql mysqldump -u root -p'password' --all-databases > backup.sql
Errores comunes
Error 1: Puerto ya en uso
Error: bind: address already in use
Solución: Otro servicio usa ese puerto. Cambia el puerto o para el servicio conflictivo.
Error 2: Permisos de volumen
Permission denied
Solución: Ajusta permisos o usa user: en el compose.
Error 3: Contenedor se reinicia constantemente
docker logs mi-contenedor
Revisa los logs para ver el error.
Error 4: Sin espacio en disco
docker system prune -a
Limpia imágenes y contenedores no usados.
Docker vs instalación tradicional
| Aspecto | Docker | Tradicional |
|---|---|---|
| Instalación | Un comando | Múltiples pasos |
| Consistencia | Siempre igual | Varía por servidor |
| Aislamiento | Total | Compartido |
| Recursos | Ligero overhead | Nativo |
| Complejidad | Curva inicial | Familiar |
| Migración | Fácil | Manual |
Cuándo usar Docker en VPS
Sí usar Docker si:
- Despliegas múltiples aplicaciones
- Necesitas entornos reproducibles
- Trabajas con microservicios
- Quieres facilitar migraciones
- Tu equipo ya conoce Docker
Quizás no si:
- Solo tienes una app simple (WordPress básico)
- VPS con muy poca RAM (<2GB)
- No tienes tiempo para aprender
- Tu hosting ya gestiona todo
Preguntas frecuentes
¿Docker consume mucha RAM?
El engine consume ~100MB. Cada contenedor suma según la app. Mínimo recomendado: 2GB de RAM en el VPS.
¿Puedo usar Docker con panel como cPanel?
Técnicamente sí, pero no es ideal. Docker funciona mejor gestionado directamente o con paneles específicos como Portainer.
¿Los contenedores son seguros?
Sí, con buenas prácticas. Aíslan aplicaciones pero comparten kernel. No es virtualización completa.
¿Puedo hacer Docker de mi WordPress existente?
Sí, pero requiere migrar datos. Para WordPress nuevo, Docker es excelente. Para existente, evalúa si vale la pena.
Nuestra recomendación
Para empezar: Docker Compose con ejemplos simples. Un WordPress dockerizado te enseña los conceptos.
Para producción: Añade Traefik para múltiples dominios y SSL automático.
Si no quieres complicarte: Los VPS de Avantys con administración gestionada pueden incluir configuración Docker profesional.
Conclusión
Docker simplifica despliegues y hace tu VPS más organizado. La curva de aprendizaje inicial vale la pena por la consistencia y facilidad de gestión que ganas.
Empieza con ejemplos simples, entiende los conceptos, y gradualmente dockeriza más servicios. En poco tiempo, no querrás volver a instalaciones tradicionales.
¿Quieres un VPS preparado para Docker? Explora los VPS de Avantys con soporte completo.
Guías relacionadas
- Primeros pasos después de contratar VPS
- Securizar un VPS Linux
- Automatizar tareas con cron
- Backups en VPS: guía completa
¿Quieres la guía completa con todos los casos de uso?
¿Quieres que lo hagamos por ti?
En Avantys gestionamos tu web, hosting y crecimiento digital de punta a punta. Tú a lo importante.