“Funciona en mi máquina” es cosa del pasado. Docker empaqueta tu aplicación con todas sus dependencias, garantizando que funcione igual en desarrollo y en producción.
Esta guía te enseña Docker desde cero, orientado a desplegar aplicaciones en tu VPS.
¿Qué es Docker y por qué usarlo?
Docker en una frase
Docker empaqueta tu aplicación + dependencias en un contenedor que funciona igual en cualquier lugar.
Sin Docker vs Con Docker
| Sin Docker | Con Docker |
|---|---|
| ”Funciona en mi máquina” | Funciona igual en todos lados |
| Instalar dependencias manualmente | Dependencias incluidas |
| Conflictos entre versiones | Cada app aislada |
| Configuración diferente por servidor | Mismo contenedor siempre |
| Difícil de replicar | Un comando para desplegar |
Conceptos clave
| Concepto | Qué es | Analogía |
|---|---|---|
| Imagen | Plantilla inmutable | Receta de cocina |
| Contenedor | Instancia de una imagen | Plato preparado |
| Dockerfile | Instrucciones para crear imagen | Pasos de la receta |
| Docker Compose | Orquestar múltiples contenedores | Menú completo |
| Volumen | Datos persistentes | Ingredientes guardados |
Instalación de Docker
Ubuntu/Debian
# Instalar dependencias
sudo apt update
sudo apt install -y ca-certificates curl gnupg
# Añadir repositorio 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
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
docker --version
Usar Docker sin sudo
sudo usermod -aG docker $USER
# Cerrar sesión y volver a entrar
# O ejecutar:
newgrp docker
# Verificar
docker run hello-world
Comandos esenciales
Imágenes
# Descargar imagen
docker pull nginx
docker pull node:20
docker pull mysql:8
# Ver imágenes descargadas
docker images
# Eliminar imagen
docker rmi nginx
Contenedores
# Ejecutar contenedor
docker run nginx
# Ejecutar en background (-d) con nombre
docker run -d --name mi-nginx nginx
# Ver contenedores activos
docker ps
# Ver 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 activo
docker rm -f mi-nginx
Puertos y volúmenes
# Mapear puerto: -p host:contenedor
docker run -d -p 8080:80 --name web nginx
# Acceder en http://IP:8080
# Montar volumen: -v host:contenedor
docker run -d -p 8080:80 \
-v /var/www/html:/usr/share/nginx/html \
--name web nginx
# Volumen persistente (Docker lo gestiona)
docker run -d \
-v mis-datos:/var/lib/mysql \
--name db mysql:8
Logs y terminal
# Ver logs
docker logs mi-nginx
docker logs -f mi-nginx # Seguir en tiempo real
# Entrar al contenedor
docker exec -it mi-nginx bash
docker exec -it mi-nginx sh # Si no tiene bash
# Ejecutar comando
docker exec mi-nginx nginx -t
Tu primer Dockerfile
Estructura básica
# Imagen base
FROM node:20-alpine
# Directorio de trabajo
WORKDIR /app
# Copiar archivos de dependencias
COPY package*.json ./
# Instalar dependencias
RUN npm ci --only=production
# Copiar código
COPY . .
# Puerto que expone
EXPOSE 3000
# Comando para ejecutar
CMD ["node", "server.js"]
Construir y ejecutar
# Construir imagen
docker build -t mi-app:1.0 .
# Ver imagen creada
docker images | grep mi-app
# Ejecutar
docker run -d -p 3000:3000 --name mi-app mi-app:1.0
Dockerfile para PHP
FROM php:8.2-fpm
# Extensiones PHP
RUN docker-php-ext-install pdo pdo_mysql mysqli
# Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/html
COPY . .
RUN composer install --no-dev --optimize-autoloader
EXPOSE 9000
CMD ["php-fpm"]
Dockerfile para Python
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]
Docker Compose
¿Por qué Docker Compose?
Cuando necesitas múltiples contenedores (app + base de datos + cache), Docker Compose los gestiona todos con un archivo.
Estructura básica
# docker-compose.yml
version: '3.8'
services:
web:
image: nginx
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
depends_on:
- db
- redis
db:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: app
volumes:
- db-data:/var/lib/mysql
redis:
image: redis:alpine
volumes:
db-data:
Comandos Docker Compose
# Iniciar todo
docker compose up -d
# Ver logs
docker compose logs -f
# Parar todo
docker compose down
# Reconstruir
docker compose build
docker compose up -d --build
# Ver estado
docker compose ps
# Ejecutar comando en servicio
docker compose exec app bash
Ejemplos prácticos
WordPress con Docker Compose
# 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: wordpress_pass
WORDPRESS_DB_NAME: wordpress
volumes:
- wp-content:/var/www/html/wp-content
depends_on:
- db
restart: unless-stopped
db:
image: mysql:8
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress_pass
MYSQL_ROOT_PASSWORD: root_pass
volumes:
- db-data:/var/lib/mysql
restart: unless-stopped
volumes:
wp-content:
db-data:
docker compose up -d
# WordPress disponible en http://IP
Node.js + MongoDB
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
MONGODB_URI: mongodb://mongo:27017/app
depends_on:
- mongo
restart: unless-stopped
mongo:
image: mongo:6
volumes:
- mongo-data:/data/db
restart: unless-stopped
volumes:
mongo-data:
Laravel + MySQL + Redis
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
volumes:
- .:/var/www/html
depends_on:
- db
- redis
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- .:/var/www/html
- ./nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- app
db:
image: mysql:8
environment:
MYSQL_DATABASE: laravel
MYSQL_USER: laravel
MYSQL_PASSWORD: secret
MYSQL_ROOT_PASSWORD: root
volumes:
- db-data:/var/lib/mysql
redis:
image: redis:alpine
volumes:
db-data:
Redes en Docker
Red por defecto
Docker Compose crea una red automática donde los servicios se comunican por nombre.
# app puede conectar a "db" y "redis" por nombre
services:
app:
environment:
DB_HOST: db
REDIS_HOST: redis
db:
image: mysql:8
redis:
image: redis
Redes personalizadas
services:
app:
networks:
- frontend
- backend
nginx:
networks:
- frontend
db:
networks:
- backend
networks:
frontend:
backend:
Volúmenes y persistencia
Tipos de volúmenes
services:
db:
volumes:
# Volumen nombrado (Docker lo gestiona)
- db-data:/var/lib/mysql
# Bind mount (carpeta del host)
- ./backup:/backup
# Archivo específico
- ./my.cnf:/etc/mysql/my.cnf:ro
volumes:
db-data:
Backup de volúmenes
# Listar volúmenes
docker volume ls
# Backup de volumen
docker run --rm \
-v mi-volumen:/source:ro \
-v $(pwd):/backup \
alpine tar czf /backup/volumen-backup.tar.gz -C /source .
# Restaurar
docker run --rm \
-v mi-volumen:/target \
-v $(pwd):/backup \
alpine tar xzf /backup/volumen-backup.tar.gz -C /target
Desplegar en producción
Variables de entorno
# docker-compose.prod.yml
services:
app:
env_file:
- .env.production
environment:
- NODE_ENV=production
# .env.production
DATABASE_URL=mysql://user:pass@db:3306/app
REDIS_URL=redis://redis:6379
SECRET_KEY=tu-secreto-muy-largo
Reinicio automático
services:
app:
restart: unless-stopped
# Opciones: no, always, on-failure, unless-stopped
Healthchecks
services:
app:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Límites de recursos
services:
app:
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
memory: 256M
SSL con Traefik
Traefik como reverse proxy
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"
- "[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
app:
image: mi-app
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`tudominio.com`)"
- "traefik.http.routers.app.entrypoints=websecure"
- "traefik.http.routers.app.tls.certresolver=letsencrypt"
volumes:
letsencrypt:
Comandos de limpieza
# Eliminar contenedores parados
docker container prune
# Eliminar imágenes sin usar
docker image prune
# Eliminar volúmenes sin usar
docker volume prune
# Eliminar todo lo no usado
docker system prune -a
# Ver espacio usado
docker system df
Preguntas frecuentes
¿Docker consume muchos recursos en un VPS pequeño?
Docker tiene overhead mínimo. Un VPS de 2GB puede correr varios contenedores pequeños. El consumo depende más de tus aplicaciones que de Docker mismo.
¿Puedo usar Docker junto con servicios instalados normalmente?
Sí, Docker coexiste con servicios nativos. Solo asegúrate de no tener conflictos de puertos. Es común tener Nginx nativo como proxy y apps en Docker.
¿Cómo actualizo una aplicación en Docker?
Construye nueva imagen con docker build, para el contenedor antiguo, e inicia el nuevo. Con Docker Compose: docker compose pull && docker compose up -d.
¿Los datos se pierden si elimino un contenedor?
Sí, a menos que uses volúmenes. Los datos en volúmenes persisten aunque elimines el contenedor. Siempre usa volúmenes para bases de datos.
¿Docker o instalación tradicional para producción?
Ambos son válidos. Docker facilita despliegues consistentes y portabilidad. La instalación tradicional puede ser más simple para una sola aplicación.
Nuestra recomendación
Para empezar:
- Instala Docker
- Prueba con
docker run nginx - Crea tu primer Dockerfile
- Usa Docker Compose para multi-contenedor
Para producción:
- Docker Compose con restart policies
- Volúmenes para datos persistentes
- Traefik o Nginx para SSL
- Backups de volúmenes
¿Necesitas ayuda con Docker? La administración gestionada de Avantys incluye configuración y mantenimiento de contenedores.
Conclusión
Docker simplifica despliegues y garantiza consistencia entre entornos. Una vez que lo dominas, no querrás volver a instalar dependencias manualmente.
Empieza con contenedores simples y ve añadiendo complejidad según necesites.
¿Necesitas un VPS para Docker? Explora los VPS de Avantys con recursos dedicados para contenedores.
¿Quieres que lo hagamos por ti?
En Avantys gestionamos tu web, hosting y crecimiento digital de punta a punta. Tú a lo importante.