Problema

Muchas pymes quieren mover una aplicación que ya funciona en Docker Compose en laptops de desarrollo a un servidor físico on‑premise con Windows Server 2022. El reto no es solo “instalar Docker”, sino armar una cadena completa: asignar discos para datos persistentes, exponer los servicios de forma segura, automatizar backups y dejar la puerta abierta a accesos remotos futuros. Cuando la infraestructura es nueva, es fácil omitir pasos críticos (por ejemplo, montar volúmenes en el disco correcto o configurar el arranque automático del daemon) y terminar con un stack que se cae al primer reinicio o que muestra advertencias de certificado en los navegadores de los empleados.

Causa

Los fallos habituales provienen de tres áreas:

  1. Elección del hipervisor – En Windows Server 2022 Docker Desktop no está licenciado; la alternativa es Docker Engine en modo “Windows containers” o usar WSL 2 como capa de Linux. Instalar ambos sin una razón clara genera conflictos de red y consumo innecesario de recursos.
  2. Persistencia de datos – Montar volúmenes sobre el disco del sistema (SSD de 500 GB) agota espacio rápidamente y dificulta la recuperación en caso de fallo del SO. La falta de una estrategia de mapeo de discos lleva a bases de datos y objetos de MinIO sin respaldo.
  3. Exposición de servicios – Un reverse proxy mal configurado o certificados auto‑firmados sin una autoridad interna provocan errores 502/SSL y hacen que los usuarios tengan que aceptar excepciones en cada navegador. Además, la ausencia de una política de backups programados deja la información vulnerable a pérdidas.

Solución

A continuación se describe una arquitectura modular que funciona tanto para un despliegue puntual como para ampliaciones futuras (acceso remoto, alta disponibilidad, etc.). La idea es mantener todo dentro de contenedores, usar Windows Server solo como host y delegar la complejidad a herramientas probadas.

1. Preparación del host

  • IP estática: asigna una dirección fija al servidor (ej. 10.0.0.20) y reserva la misma en el DHCP del router.

  • Particionado de discos:

    • SSD 500 GB → sistema operativo y contenedores de aplicación.
    • HDD 2 TB → volúmenes persistentes (postgres_data, minio_data).
      Formatea el HDD con NTFS y crea dos carpetas (D:\postgres y D:\minio). No uses permisos heredados; deja que Docker los administre.
  • Hipervisor: habilita Hyper‑V (no WSL 2) porque Windows Server ya incluye el rol y permite una integración nativa con Docker Engine.

    Install-WindowsFeature -Name Hyper-V -IncludeManagementTools -Restart
    
  • Docker Engine: descarga el paquete MSI de Docker Engine para Windows Server desde la página oficial y sigue la instalación guiada. Después de la instalación, verifica la versión y habilita el arranque automático:

    Set-Service -Name com.docker.service -StartupType Automatic
    
  • Git: instala Git for Windows (MSI) para clonar el repositorio directamente en el SSD (C:\apps\myapp).

2. Configuración del reverse proxy y TLS

  • Elección del proxy: Caddy es una opción ligera que genera automáticamente certificados con Let’s Encrypt, pero en un entorno cerrado sin acceso a internet es más práctico usar Nginx con certificados internos.

  • CA interna: si la empresa ya cuenta con Active Directory Certificate Services, usa esa CA. En su ausencia, mkcert es suficiente para crear una CA local y firmar certificados que los equipos pueden confiar añadiendo el root cert al almacén de Windows.

    mkcert -install
    mkcert myapp.local "*.myapp.local"
    
  • Copia los archivos myapp.local+2-key.pem y myapp.local+2.pem a C:\proxy\certs.

  • Docker‑compose para el proxy (fragmento):

    version: "3.9"
    services:
      nginx:
        image: nginx:alpine
        ports:
          - "80:80"
          - "443:443"
        volumes:
          - ./nginx.conf:/etc/nginx/nginx.conf:ro
          - C:/proxy/certs:/etc/nginx/certs:ro
        restart: unless-stopped
    

    En nginx.conf define los upstreams hacia los contenedores de Node y React y habilita TLS con los certificados montados.

3. Despliegue de la aplicación

  • Estructura de carpetas:

    C:\apps\myapp\
      docker-compose.yml
      .env
    D:\postgres\   ← volúmenes persistentes
    D:\minio\      ← volúmenes persistentes
    
  • En docker-compose.yml mapea los volúmenes explícitamente:

    services:
      postgres:
        image: postgres:14
        volumes:
          - D:/postgres:/var/lib/postgresql/data
      minio:
        image: minio/minio
        command: server /data
        volumes:
          - D:/minio:/data
    
  • Ejecuta:

    cd C:\apps\myapp
    docker compose up -d
    

    Docker Engine iniciará los contenedores y el proxy comenzará a escuchar en 80/443.

4. Automatización de backups

  • PostgreSQL: usa pg_dump dentro de un contenedor temporal para crear un dump comprimido y guardarlo en una carpeta de backups (D:\backups\postgres).

  • MinIO: copia el árbol de objetos con robocopy o xcopy a la misma ubicación de backups.

  • Programa una tarea en el Programador de tareas de Windows que ejecute un script PowerShell cada noche a las 02:00 h.

    $date = Get-Date -Format "yyyyMMdd_HHmm"
    $pgDump = "docker run --rm --network host -e PGPASSWORD=$env:PGPASS postgres:14 pg_dump -U $env:PGUSER -d $env:PGDATABASE > D:\backups\postgres\dump_$date.sql"
    $minioCopy = "robocopy D:\minio D:\backups\minio /MIR /R:2 /W:5"
    Invoke-Expression $pgDump
    Invoke-Expression $minioCopy
    

5. Preparación para acceso remoto

Para abrir la aplicación a usuarios fuera de la oficina sin exponer directamente los puertos del servidor, considera:

  • Tailscale: crea una red mesh basada en WireGuard; los clientes solo necesitan instalar el cliente y se conectan al servidor con su IP de Tailscale. El tráfico queda cifrado de extremo a extremo y no requiere cambios en el firewall.
  • Cloudflare Tunnel (antes Argo Tunnel): si la empresa tiene un dominio en Cloudflare, lanzar cloudflared como contenedor permite exponer myapp.local mediante un subdominio público sin abrir puertos.
  • Port forwarding controlado: como último recurso, abre solo 443 en el router y limita el origen a rangos IP conocidos.

Cuándo aplicar esta solución

  • Escenario típico: una pequeña oficina (≤ 20 usuarios) con un servidor físico Windows y una aplicación Docker Compose que necesita persistencia y SSL interno.
  • Síntomas que indican la necesidad: caídas tras reinicio del host, errores de certificado en navegadores, falta de espacio en el disco del sistema, backups manuales que se olvidan.
  • No aplicar: entornos que requieren orquestación a gran escala (Kubernetes) o alta disponibilidad multi‑nodo; en esos casos conviene migrar a una solución de clúster.

Código

# 1. Instalar Docker Engine (Windows Server)
Invoke-WebRequest -Uri "https://download.docker.com/components/engine/windows-server/docker-20.10.24.zip" -OutFile "docker.zip"
Expand-Archive docker.zip -DestinationPath "C:\Program Files\Docker"
& "C:\Program Files\Docker\dockerd.exe" --register-service
Start-Service com.docker.service
Set-Service -Name com.docker.service -StartupType Automatic

# 2. Clonar proyecto y lanzar stack
git clone https://github.com/empresa/myapp.git C:\apps\myapp
cd C:\apps\myapp
docker compose up -d

# 3. Generar certificados internos con mkcert
mkcert -install
mkcert myapp.local "*.myapp.local"

# 4. Tarea programada de backup (PowerShell script guardado como backup.ps1)
$script = @"
$date = Get-Date -Format 'yyyyMMdd_HHmm'
docker run --rm --network host -e PGPASSWORD=$env:PGPASS postgres:14 pg_dump -U $env:PGUSER -d $env:PGDATABASE > D:\backups\postgres\dump_$date.sql
robocopy D:\minio D:\backups\minio /MIR /R:2 /W:5
"@
Set-Content -Path "C:\scripts\backup.ps1" -Value $script
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-File C:\scripts\backup.ps1"
$trigger = New-ScheduledTaskTrigger -Daily -At 02:00
Register-ScheduledTask -TaskName "DailyBackup" -Action $action -Trigger $trigger -User "SYSTEM" -RunLevel Highest

Verificación

  1. Estado del daemon: docker info debe mostrar “Server Version” y “Operating System: Windows Server 2022”.
  2. Contenedores activos: docker ps debe listar nginx, node, react, postgres y minio.
  3. Acceso HTTPS: abre https://myapp.local desde una máquina cliente; el navegador no debe mostrar advertencias si el certificado raíz de mkcert está instalado.
  4. Persistencia: crea una tabla en PostgreSQL, sube un archivo a MinIO, reinicia el host y verifica que los datos siguen presentes.
  5. Backup: revisa la carpeta D:\backups después de la ejecución de la tarea programada; debe haber un archivo .sql y una copia espejo de D:\minio.

Notas adicionales

  • Permisos de carpetas: Docker necesita acceso completo a los volúmenes montados.