Trabajar con contenedores Docker en servidores remotos se ha convertido en el pan de cada día para cualquiera que quiera desplegar aplicaciones modernas sin volverse loco con las dependencias, las versiones de librerías y los clásicos “en mi máquina funciona”. Sin embargo, cuando pasamos de ejecutar un simple docker run en local a montar un despliegue serio en un servidor Linux, con Docker Compose, acciones de GitHub, Plesk, Portainer o incluso aplicaciones gráficas accesibles por navegador, la cosa se complica un poco.
Si tu objetivo es desplegar contenedores Docker en un servidor remoto (Ubuntu, Debian, Windows con WSL 2, un Cloud Server, Plesk, etc.) y hacerlo de forma mantenible, automatizada y segura, en esta guía tienes un recorrido bastante completo: desde el uso básico de Docker Compose en remoto, hasta entornos de desarrollo con VS Code, despliegues desde Plesk, administración con Portainer y ejecución remota de aplicaciones gráficas usando noVNC y Caddy.
Conceptos básicos: contenedores Docker y despliegue remoto
Docker es una plataforma de contenedores que empaqueta una aplicación junto con todo lo que necesita (librerías, dependencias, binarios, configuración mínima del sistema) para que se ejecute igual en cualquier máquina que tenga instalado el motor de Docker. La diferencia clave respecto a una máquina virtual es que el contenedor no trae un sistema operativo completo, sino que comparte el kernel del host, lo que se traduce en imágenes más ligeras y mejor rendimiento.
Para desplegar contenedores Docker en servidores remotos lo habitual es tener un host (por ejemplo, un servidor Ubuntu en la nube) con Docker y, opcionalmente, Docker Compose, y luego enviarle el código o las imágenes para que las ejecute allí. Eso puedes hacerlo a mano por SSH, automatizarlo con GitHub Actions o integrarlo con paneles como Plesk o herramientas como Portainer.
Los principales escenarios reales que vas a encontrarte cuando hablas de “Docker remoto” son tres: desarrollo local pero ejecutando contenedores en otra máquina (o en WSL 2), despliegue automatizado de servicios backend/frontend y administración de contenedores de producción (monitorización, logs, reinicios, políticas de red, etc.). La tecnología es la misma, lo que cambia es la forma de orquestarla.
Además de servicios de backend de toda la vida, Docker permite algo menos conocido pero muy potente: ejecutar aplicaciones gráficas (clientes de correo, IDEs, herramientas de análisis, etc.) dentro de contenedores remotos y acceder a ellas desde el navegador usando VNC sobre WebSocket. Es una forma cómoda de aprovechar servidores potentes cuando tu PC se queda corto.
De docker run a Docker Compose en un servidor remoto

Un patrón bastante común de despliegue manual consiste en tener un repositorio de código en GitHub, un servidor remoto Ubuntu con Docker instalado y un flujo de CI/CD (por ejemplo, GitHub Actions) que hace lo siguiente cuando haces push a una rama como development o main:
- Conectar al servidor remoto por SSH.
- Detener y eliminar los contenedores en ejecución.
- Descargar las imágenes nuevas desde Docker Hub (o desde tu registro privado).
- Ejecutar
docker runpara cada servicio. - Dejar que Nginx (o el proxy inverso que uses) redirija el tráfico a los puertos de cada contenedor.
Cuando pasas a Docker Compose el flujo se simplifica bastante, porque en lugar de manejar contenedores uno a uno, defines toda tu stack (frontend, backend, base de datos, cache, etc.) en un único docker-compose.yml, con sus redes, volúmenes y variables de entorno.
La práctica más sencilla (y bastante habitual) con GitHub Actions es que, tras establecer la conexión SSH, el workflow haga un cd al directorio del proyecto en el servidor remoto (donde vive tu docker-compose.yml) y ejecute comandos del tipo:
docker compose pullpara traer las últimas imágenes.docker compose downpara parar y eliminar contenedores antiguos (opcionalmente con--remove-orphans).docker compose up -d --buildsi construyes imágenes desde el propio servidor.
Este enfoque funciona bien y es bastante robusto, siempre que controles bien credenciales, variables de entorno y volúmenes. Puedes mejorar la seguridad evitando que las GitHub Actions tengan acceso root completo al servidor, restringiendo claves SSH, usando usuarios dedicados o incluso exponiendo Docker como servicio remoto seguro con certificados, en lugar de ejecutar todo por SSH.
No hay una “forma mágica” mucho mejor que esto para un entorno sencillo: la clave es automatizar y escribir bien el docker-compose.yml, usar etiquetas de imagen inmutables (por ejemplo, versiones concretas) y tener algún mecanismo de rollback (por ejemplo, guardando la versión anterior del compose o de las imágenes).
Entorno de desarrollo remoto con Docker, WSL 2 y VS Code

En Windows es muy habitual desarrollar con Docker apoyándote en WSL 2. Docker Desktop para Windows ofrece un motor basado en WSL 2 que te permite correr contenedores Linux y Windows desde la misma máquina, mientras editas código con VS Code y pruebas en el navegador local.
El flujo típico para montar un entorno de desarrollo con contenedores remotos usando WSL 2 es el siguiente:
- Instalas WSL 2 y una distro Linux (Ubuntu, por ejemplo).
- Instalas Docker Desktop en Windows y activas la opción de “Usar el motor basado en WSL 2” en Ajustes > General.
- En Ajustes > Recursos > WSL Integration, marcas las distribuciones WSL en las que quieres que Docker funcione.
- Compruebas la instalación con
docker --versiony ejecutandodocker run hello-worlddentro de la distro WSL.
Para desarrollar “dentro” de los contenedores y no sólo usar Docker como motor, VS Code es clave. Con las extensiones WSL, Dev Containers y Docker puedes hacer cosas como:
- Abrir una carpeta de tu proyecto alojada dentro de WSL directamente en VS Code.
- Reabrir esa carpeta “en un contenedor de desarrollo” (Dev Container), usando un
Dockerfiley undevcontainer.jsonque describen tu entorno ideal (versión de Python, Node, etc.). - Depurar tu aplicación desde VS Code mientras se ejecuta en el contenedor.
Un ejemplo muy habitual es trabajar con un proyecto Django o Node.js: clonas el repositorio dentro de WSL, abres la carpeta con code ., seleccionas una definición de Dev Container (por ejemplo, “Python 3”) y VS Code construye la imagen y levanta el contenedor con todas las dependencias. Desde ahí, puedes ejecutar, depurar y comprobar que el código corre en Linux aunque tu sistema host sea Windows.
Este enfoque también es útil cuando tu máquina no es muy potente, porque puedes mover parte de la carga a un servidor remoto con Docker y conectarte a él con VS Code via SSH y Dev Containers, trabajando casi como si fuera local pero apoyándote en los recursos del servidor.
Desplegar aplicaciones en un servidor Cloud con Docker y Docker Compose
Montar un servidor cloud con Docker listo para desplegar es muy rápido en la mayoría de proveedores: eliges una imagen de sistema que ya trae Docker preinstalado, esperas unos minutos y tienes la máquina lista para recibir contenedores.
Un patrón típico para desplegar una aplicación Node.js sencilla sería este:
- Crear el proyecto Node.js (por ejemplo, un “Hola mundo” con Express) en tu máquina local: carpeta del proyecto, subdirectorio
app,npm init, instalación de dependencias (comoexpress) y unindex.jsque levante un servidor en el puerto 3030 con un mensaje básico. - Dockerizar la app con un Dockerfile que defina la imagen base (por ejemplo
node:12), elWORKDIR, copie los archivos de la app, ejecutenpm instally exponga el puerto interno. - Añadir un .dockerignore para evitar subir al contenedor cosas como
node_modules. - Crear un docker-compose.yml en la raíz del proyecto, indicando la versión (por ejemplo,
3.8) y definiendo el servicio principal, subuild, el mapeo de puertos (3030:3030) y el comando (node index).
Una vez que tienes el proyecto y el compose listos, el despliegue en el servidor remoto suele seguir este flujo:
- Te conectas por SSH al servidor.
- Clonas el repositorio o subes los archivos (Git, SCP, rsync…).
- Instalas docker-compose si no estaba ya (en muchas distros hay que instalarlo aparte de Docker, por ejemplo descargando el binario desde GitHub y dándole permisos de ejecución).
- Ejecutas
docker-compose up(odocker compose upsegún la versión) para que se descarguen las imágenes, se construya la imagen de tu app y se levanten los contenedores.
Un punto que a menudo se pasa por alto es el firewall del proveedor: si tu servicio escucha en el puerto 3030, tendrás que abrirlo en las reglas de firewall o crear una política específica y asociarla al servidor. Si no, desde fuera sólo verás un “connection refused” aunque el contenedor esté bien levantado.
Una vez operativo, puedes acceder a la aplicación usando la IP pública del servidor y el puerto expuesto (por ejemplo, https://IP_DEL_SERVIDOR:3030), o bien escondiendo ese puerto detrás de un proxy inverso como Nginx/Traefik que atienda en el puerto 80/443.
Gestión de contenedores remotos con Plesk y Docker
Si utilizas Plesk como panel de control, también puedes apoyarte en su extensión de Docker para manejar contenedores directamente desde la interfaz web, tanto en el propio servidor como en hosts Docker remotos.
Plesk soporta Docker en una buena variedad de sistemas operativos: CentOS 7, RHEL 7, Debian 10/11/12, distintas versiones de Ubuntu (18.04, 20.04, 22.04, 24.04), AlmaLinux 8/9, Rocky Linux 8.x y Virtuozzo 7 actualizado. En Plesk para Windows, Docker no se ejecuta localmente sino en una máquina remota que actúa como host Docker.
Hay algunas limitaciones importantes a tener en cuenta:
- No puedes usar la extensión Docker si Plesk está desplegado dentro de un contenedor Docker.
- Para usar servicios remotos de Docker (es decir, hosts externos), necesitas una licencia adicional o packs concretos (Hosting Pack, Power Pack, Developer Pack).
- Los contenedores Docker gestionados por Plesk no son “migrables” como tal, aunque sí puedes respaldar los datos que usan mediante volúmenes o instantáneas.
Desde la interfaz de Plesk puedes buscar imágenes tanto en el repositorio local (imágenes ya descargadas en el host) como en Docker Hub. Para lanzar un contenedor, el panel te guía:
- Ir a Docker > Contenedores > Ejecutar contenedor.
- Buscar la imagen deseada y revisar su documentación en Docker Hub (si aplica).
- Seleccionar, opcionalmente, una etiqueta/version concreta de la imagen.
- Configurar parámetros del contenedor (variables de entorno, puertos, volúmenes, memoria, arranque automático, etc.) y pulsar en Ejecutar.
Plesk también te permite gestionar la configuración avanzada de cada contenedor: reasignar puertos (automáticos o manuales), decidir si el puerto es accesible desde Internet o sólo desde localhost, limitar la memoria RAM que puede consumir el contenedor, definir volúmenes (ruta en el host y en el contenedor) o añadir tantas variables de entorno como necesites.
En cuanto a la parte de orquestación remota, Plesk puede trabajar con “servicios Docker remotos”. Esto implica configurar el daemon de Docker en el host remoto (por ejemplo, mediante un /etc/docker/daemon.json con soporte para TLS y sockets TCP), generar certificados .pem y registrar ese host en Plesk desde Docker > Entornos > Añadir servidor. Después puedes marcar ese nodo Docker como activo y alternar entre distintos servidores desde la misma interfaz.
Desplegar pilas de Docker Compose desde Plesk
Si ya trabajas tu infraestructura con Docker Compose, te puede interesar que sea Plesk el que se encargue de desplegar “pilas” a partir de archivos docker-compose.yml.
El flujo para desplegar un Compose en Plesk es relativamente directo:
- Entrar en Docker > Pilas > Añadir pila.
- Asignar un nombre de proyecto a la pila.
- Elegir el origen del archivo Compose: editor (pegar el contenido), subir un archivo desde tu ordenador o seleccionar un archivo existente en el espacio web de un dominio.
- Confirmar la configuración y permitir que Plesk declare y cree los contenedores definidos.
Todo lo que se construya durante el proceso de build asociado a ese Compose se almacena en el directorio principal del sitio web, lo cual facilita el acceso a logs, artefactos intermedios o ficheros adicionales que haya generado el build.
Plesk también facilita la gestión de imágenes locales: desde Docker > Imágenes puedes filtrar, revisar las distintas etiquetas de un producto, ver el espacio ocupado y eliminar imágenes obsoletas para liberar disco. Esto es importante en entornos remotos con espacio limitado.
Si usas Nginx como servidor web frontal, Plesk aplica reglas de proxy (por ejemplo, en el nginx.conf del dominio) para enrutar tráfico hacia tus contenedores Docker, incluso en escenarios detrás de NAT. Esto te evita pelearte a mano con configuraciones de proxy inverso en servidores remotos.
Administrar contenedores remotos con Portainer
Portainer es una interfaz web ligera para Docker que simplifica bastante el día a día de quien no quiere vivir en la línea de comandos. Funciona él mismo como un contenedor y puede administrar el host local o varios hosts remotos (incluso con Portainer Agent).
Para instalar Portainer en tu servidor con Docker sueles seguir estos pasos básicos:
- Crear un volumen para los datos de Portainer:
docker volume create portainer_data. - Lanzar el contenedor de Portainer mapeando los puertos 8000 y 9000, montando el socket de Docker (
/var/run/docker.sock) y el volumen de datos:docker run -d -p 8000:8000 -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer.
Con esto tendrás Portainer escuchando en el puerto 9000 del servidor. Como siempre, toca abrir el puerto en el firewall o exponerlo a través de un proxy inverso HTTPS. La primera vez que entras por navegador, Portainer te pedirá crear un usuario administrador y, luego, podrás elegir si vas a gestionar el Docker local o hosts remotos adicionales.
El panel de Portainer es bastante intuitivo: verás los contenedores activos, sus logs, estadísticas de consumo de recursos, stacks de Compose, redes, volúmenes, etc. Y, sobre todo, te permite recrear contenedores con otros parámetros, actualizar imágenes, gestionar stacks y centralizar varios servidores remotos desde una sola interfaz.
Ejecutar aplicaciones gráficas en contenedores remotos y acceder por navegador
Cuando tu ordenador se queda corto de recursos pero necesitas usar aplicaciones gráficas pesadas (como clientes de correo, IDEs o herramientas de ingeniería inversa), una solución muy interesante es ejecutarlas en contenedores Docker en un servidor potente y acceder a ellas vía web.
Un caso práctico muy bien documentado es el de encapsular Mozilla Thunderbird en un contenedor, exponer su interfaz gráfica a través de TigerVNC/noVNC y proteger el acceso con Caddy. La idea se puede reutilizar para casi cualquier aplicación GUI de Linux.
La arquitectura base de este tipo de contenedores gráficos suele incluir:
- Un servidor VNC/X11 ligero (TigerVNC) que actúa como servidor de visualización.
- Un gestor de ventanas minimalista (OpenBox) para manejar las ventanas.
- Un pequeño servidor fácil de usar tipo
easy-novncque expone el VNC como WebSocket y genera una página HTML para conectarse desde el navegador. supervisordo similar para arrancar y supervisar todos los procesos dentro del contenedor.- La aplicación en sí (Thunderbird, GIMP, etc.) configurada para mostrarse en el display remoto (
DISPLAY=:0).
En la práctica se monta un directorio de trabajo (por ejemplo, ~/thunderbird) donde se colocan:
- Un supervisord.conf que define los programas a lanzar: TigerVNC, easy-novnc, OpenBox y la aplicación principal, con prioridades para que el servidor gráfico arranque antes de la app.
- Un menu.xml de OpenBox que configura el menú del escritorio (aplicación principal, terminal, monitor de procesos con htop, etc.).
- Un Dockerfile multi-etapa que en la primera etapa compila
easy-novnccon Go, y en la segunda crea la imagen final sobre Debian, instalando openbox, tigervnc, supervisor, utilidades de consola, la aplicación (Thunderbird en el ejemplo), copiando los binarios y configuraciones, creando un usuario no root y definiendo un volumen de datos persistente en/data.
El comando por defecto del contenedor suele delegarse en supervisord, arrancándolo como un usuario normal mediante gosu, tras ajustar permisos sobre el volumen de datos. Así, cuando el contenedor se levanta, automáticamente se lanzan VNC, noVNC, el gestor de ventanas y la aplicación, y tú sólo necesitas acceder al puerto HTTP que expone easy-novnc.
Para hacerlo más robusto y amigable de cara a Internet, es buena idea poner delante un servidor web como Caddy, también en contenedor, que haga de reverse proxy hacia tu app gráfica, añada autenticación básica (nombre de usuario y contraseña hasheada) y, opcionalmente, exponga un WebDAV para acceder a los archivos de /data desde tu máquina local.
Orquestar la solución con redes, volúmenes y Caddy
Para mantener ordenado este tipo de despliegue lo normal es crear una red Docker propia y uno o varios volúmenes de datos:
- Red, por ejemplo
thunderbird-net, que compartirán todos los contenedores relacionados. - Volumen
thunderbird-dataque contendrá el perfil de usuario y datos persistentes de la app gráfica.
El contenedor de la aplicación gráfica se puede lanzar con algo como:
- Política
--restart=alwayspara que se levante solo. - Montaje del volumen
thunderbird-data:/data. - Conexión a la red
thunderbird-net. - Nombre identificable (
thunderbird-app, por ejemplo) que usará Caddy para el proxy inverso.
En otro directorio (por ejemplo, ~/caddy) se construye la imagen de Caddy con los módulos necesarios (como el plugin de WebDAV) y un Caddyfile que define:
- Un servidor en el puerto 8080.
- Un
reverse_proxyhaciathunderbird-app:8080(o el puerto que exponga noVNC). - Rutas adicionales para navegar por archivos (
/files) y para WebDAV (/webdav), ambas sirviendo el contenido del volumen de datos. - Un bloque de
basicauthque protege todos los caminos con un usuario y una contraseña hasheada leída de variables de entorno.
Al crear el contenedor de Caddy, se monta el mismo volumen thunderbird-data:/data, se le conecta a la red thunderbird-net y se publica su puerto (por ejemplo, -p 8080:8080) en el host. Con esto, sólo tienes que ir en el navegador a http://IP_DEL_SERVIDOR:8080, introducir las credenciales y hacer clic en conectar para empezar a usar la aplicación gráfica de forma remota.
El mantenimiento a largo plazo es sencillo: cuando necesites actualizar, puedes parar y eliminar los contenedores, reconstruir las imágenes con las nuevas versiones y volver a lanzar los docker run manteniendo el volumen de datos, de forma que la configuración del usuario y sus archivos sigan intactos.
Con todas estas piezas (Docker Compose, Plesk, Portainer, VS Code, WSL 2, Caddy, noVNC…) es posible montar desde despliegues backend sencillos hasta escritorios remotos encapsulados en contenedores y perfectamente accesibles vía navegador, aprovechando servidores con mucha más potencia que tu máquina y manteniendo un control bastante fino sobre redes, seguridad, almacenamiento y actualizaciones.