Cómo desplegar un contenedor Docker en un servidor remoto

Última actualización: 28/01/2026
Autor: Isaac
  • Docker permite ejecutar contenedores en servidores remotos, descargando el trabajo pesado de tu equipo local y centralizando el despliegue.
  • Docker Compose simplifica la orquestación de múltiples servicios en un único archivo y facilita su actualización en servidores Linux y paneles como Plesk.
  • Herramientas como GitHub Actions, Portainer, Plesk y Docker Desktop con WSL 2 ayudan a automatizar y gestionar contenedores remotos de forma más cómoda.
  • Es posible encapsular incluso aplicaciones gráficas completas en contenedores y acceder a ellas por navegador mediante VNC/noVNC y servidores web como Caddy.

desplegar un contenedor docker en un servidor remoto

Si acabas de empezar con Docker y te estás peleando con cómo desplegar tus contenedores en un servidor remoto, tranquilo: es una duda muy habitual. Entre Docker, Docker Compose, despliegues por SSH, GitHub Actions, Plesk, Docker Desktop, Portainer… la cantidad de opciones puede abrumar, sobre todo cuando vienes de ejecutar todo “a mano” con unos cuantos docker run en tu máquina local.

En este artículo vamos a recorrer, paso a paso, las distintas formas de levantar contenedores Docker en servidores remotos Linux o Windows, cómo automatizar el despliegue con Docker Compose y CI/CD, y qué alternativas tienes si tu ordenador es muy flojito y prefieres que todo el peso se lo coma la nube. Verás también ejemplos completos con Node.js, aplicaciones web y hasta entornos gráficos remotos dentro de un contenedor accesibles desde el navegador.

Conceptos básicos: contenedores locales vs contenedores remotos

Antes de entrar en harina conviene tener claro que un contenedor Docker se ejecuta siempre donde está el motor Docker: si tu servidor remoto tiene Docker instalado, tus contenedores correrán allí; tu portátil solo hará de cliente para enviar imágenes y comandos (por SSH, Docker API o herramientas como GitHub Actions, Plesk o Portainer).

Desde el punto de vista de recursos, esto es justo lo que buscas si tienes un equipo poco potente: puedes desarrollar ligero en local y ejecutar los contenedores “pesados” en un servidor remoto, que es quien pone la CPU, RAM y disco. Tu máquina solo necesita un cliente de Docker, un navegador y, si quieres, un editor como VS Code.

En este esquema aparece un elemento clave: cómo te conectas al motor Docker remoto. Hay varias posibilidades: entrar por SSH al servidor y ejecutar los comandos allí, exponer la API TCP de Docker (con TLS) para gestionarlo a distancia, apoyarte en paneles como Plesk, usar Portainer o tirar de workflows de CI/CD como GitHub Actions que lancen el despliegue por ti.

De docker run a Docker Compose en un servidor remoto

Un escenario muy típico es este: tienes una app que desplegabas con varios docker run en un servidor Ubuntu remoto, y ahora has pasado a Docker Compose para definir todos los servicios en un único archivo. El flujo suele ser simple: cada vez que haces push a la rama de desarrollo en GitHub, se lanza una GitHub Action que se conecta por SSH al servidor, para contenedores antiguos, limpia y levanta los nuevos.

El patrón manual clásico suele ser algo como: conexión SSH al servidor, descarga de imágenes desde Docker Hub, parada y eliminación de contenedores antiguos y arranque de nuevos contenedores con los puertos adecuados para que Nginx proxyfique el tráfico. Funciona, pero se vuelve incómodo en cuanto la aplicación crece.

Con Docker Compose el enfoque no cambia tanto, pero se simplifica: lo ideal es que el archivo docker-compose.yml viva en el servidor (o se clone el repo allí) y, tras hacer cd al directorio del proyecto, lances docker compose pull y docker compose up -d --remove-orphans. Esto reemplaza todo el baile de docker run y te asegura que la pila completa se recrea de forma consistente.

Para automatizarlo con GitHub Actions, un flujo muy razonable es que el workflow haga uso de una acción de SSH remoto (o se configure un runner self-hosted en el propio servidor) y ejecute allí los comandos de Compose. Si has versionado tanto el código como el propio docker-compose.yml, basta con un git pull en el servidor seguido de los comandos de Compose para desplegar la nueva versión.

docker compose en servidor remoto

Crear un servidor remoto con Docker listo para desplegar

El primer paso para trabajar con contenedores remotos es disponer de un servidor (físico, VPS o cloud) con Docker instalado. Muchos proveedores permiten desplegar directamente imágenes con Docker preinstalado, de modo que en unos minutos tienes una máquina con el daemon listo para recibir contenedores.

Si partes de una VM limpia, el proceso habitual en Linux es instalar el motor Docker y, después, instalar Docker Compose (en algunas distros forma parte del paquete, en otras hay que bajar el binario). En sistemas tipo Debian o Ubuntu puedes usar un curl al binario oficial y darle permisos de ejecución:

sudo curl -L ‘https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m)’ -o /usr/local/bin/docker-compose

sudo chmod +x /usr/local/bin/docker-compose

A partir de aquí, con el demonio Docker funcionando, ya puedes clonar tu repositorio o subir el código por SCP/rsync y comenzar a levantar contenedores en remoto. Si tu proveedor tiene cortafuegos propios (firewall de la plataforma), revisa que los puertos que expondrán tus contenedores estén abiertos.

Ejemplo sencillo: dockerizar una app Node.js y desplegarla

Para verlo más claro, imagina una aplicación muy básica de Node.js con Express. En tu máquina de desarrollo creas la carpeta del proyecto, dentro una subcarpeta app, y ahí inicializas el proyecto con npm init e instalas Express:

npm install express

El código más simple en un index.js podría ser un “hola mundo” que escucha en el puerto 3030. En local, lo arrancas con node index y ves la app en https://localhost:3030. Hasta aquí nada nuevo.

  Linux 6.16: problemas con placas base ASUS, por qué ocurre y cómo mitigarlo sin dolores de cabeza

El siguiente paso es crear un Dockerfile en la carpeta de la app, donde defines la imagen base (por ejemplo, node:12), el directorio de trabajo, copias el código dentro del contenedor, instalas dependencias y expones el puerto:

FROM node:12 · WORKDIR /usr/src/app · ADD . /usr/src/app · RUN npm install · EXPOSE 3030

Conviene además crear un .dockerignore para evitar que node_modules y otros directorios pesados se copien a la imagen. Una vez definida la imagen, puedes construirla y probarla en local con docker build y docker run antes de irte al servidor remoto.

ejemplo aplicacion node con docker

Orquestar varios servicios con Docker Compose

Cuando tu aplicación ya no es solo un backend Node, sino que incluye frontend, API, base de datos y quizá servicios auxiliares, lo normal es definirlos todos en un archivo docker-compose.yml en la raíz del proyecto. Aunque tu ejemplo real use un solo contenedor, entender Compose como herramienta de orquestación te abre la puerta a diseños más completos.

Un ejemplo mínimo de Compose para el “hola mundo” de Express puede ser algo así: se especifica la versión de Compose, un servicio llamado express que se construye desde la carpeta ./app, se mapea el puerto 3030 interno al 3030 externo y se indica el comando de arranque:

version: ‘3.8’ · services: · express: · build: ./app · ports: – ‘3030:3030’ · command: node index

Esta definición se transporta tal cual al servidor remoto. Una vez tengas el proyecto en la máquina (clonado desde Git o copiado a mano), basta con instalar docker-compose, situarte en la carpeta del proyecto y lanzar:

docker-compose up

La primera vez tardará más, porque se descargan la imagen base de Node y las dependencias de npm. A partir de entonces, cualquier despliegue futuro es tan simple como un docker-compose up -d –build para recrear los servicios sin parar demasiado tiempo la app.

Exponer la aplicación: puertos, firewall y Nginx

Con los contenedores arrancados en el servidor remoto, aún te falta un detalle importante: asegurar que el tráfico externo llega al puerto adecuado. Si tu app escucha en el puerto 3030 y no tienes un proxy inverso, necesitas que el firewall del servidor o de la nube permita conexiones entrantes a ese puerto.

En muchos proveedores se configuran políticas de firewall a nivel de panel (por ejemplo, en Cloudbuilder o Virtuozzo), donde creas una regla para abrir el puerto 3030/TCP y asociarla a tu servidor. En servidores con firewall local (ufw, firewalld, iptables) deberás permitir ese puerto concreto o, si usas Nginx y solo quieres exponer el 80/443, configurar Nginx como proxy inverso hacia el contenedor.

Una vez el puerto esté abierto, podrás acceder a tu app en https://IP_DEL_SERVIDOR:3030. En entornos más elaborados lo habitual es que Nginx o Apache actúen de proxy inverso sobre el puerto 80/443 y redirijan a tus contenedores internos (internamente pueden usar puertos altos o incluso redes Docker personalizadas).

Si gestionas el servidor con Plesk, parte de esta lógica se integra en la propia herramienta: las reglas de proxy se definen en la configuración de Nginx del dominio, por ejemplo mediante directivas en nginx.conf bajo /var/www/vhosts/system/$domain/conf/, y Plesk se encarga de que las peticiones al dominio pasen al contenedor correcto.

Administrar contenedores remotos con Portainer

Para quienes prefieren una interfaz gráfica, Portainer es una opción muy interesante. Se trata de un panel web que corre en un contenedor y se conecta al daemon Docker (local o remoto) para mostrar contenedores, imágenes, stacks, volúmenes, etcétera, de forma muy visual.

La instalación básica en tu servidor se resume en crear primero un volumen para los datos de Portainer y luego ejecutar el contenedor exponiendo los puertos 8000 y 9000, y montando el socket de Docker:

docker volume create portainer_data

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

Tras eso, el panel queda disponible en el puerto 9000 del servidor. Accedes con el navegador, creas el usuario administrador la primera vez y ya puedes gestionar contenedores locales o añadir nodos remotos (usando la Docker API o el Portainer Agent) para centralizar la administración de varios servidores.

Una vez configurado, puedes ver de un vistazo qué contenedores están corriendo, revisar logs, recursos consumidos o crear stacks usando archivos Docker Compose sin tocar la línea de comandos.

Usar Docker en remoto con Plesk

Si en lugar de administrar el servidor a pelo usas Plesk, tienes otra vía para gestionar contenedores Docker sin salir del panel. Plesk soporta Docker en varias distribuciones Linux (CentOS 7, RHEL 7, Debian 10/11/12, Ubuntu 18.04/20.04/22.04/24.04, AlmaLinux 8/9, Rocky 8, etc.) y también admite el uso de Docker remoto desde Plesk para Windows, siempre que cuentes con la licencia correspondiente.

Desde la sección Docker del panel puedes buscar imágenes tanto en el repositorio local de la máquina como en Docker Hub, seleccionar la versión concreta mediante etiquetas y lanzar contenedores con la configuración que necesites: variables de entorno, límites de memoria, asignación de puertos y volúmenes, política de reinicio, etc.

Por defecto, Plesk puede hacer asignación automática de puertos, enlazando el puerto interno del contenedor con uno aleatorio en el host (por ejemplo 32768) y limitando el acceso a localhost para mantenerlo oculto de Internet. Si desmarcas la opción de puerto inaccesible por Internet, Docker expone ese puerto en todas las interfaces del host y la app queda accesible desde fuera.

  Uso de comandos ifconfig y route en Linux y su sustituto iproute2

El panel permite también montar directorios del servidor dentro del contenedor (volúmenes) para conseguir almacenamiento persistente que sobreviva a recreaciones del contenedor. Del mismo modo, puedes ajustar variables de entorno, renombrar contenedores, recrearlos con otra versión de la imagen, guardar el estado actual como nueva imagen o incluso descargar una instantánea.

Además de contenedores sueltos, Plesk soporta pilas Docker Compose: puedes ir a Docker > Pilas > Añadir pila, darle un nombre de proyecto y elegir si pegas el contenido del archivo Compose, lo subes desde tu equipo o lo seleccionas desde el espacio web de un dominio. A partir de ahí, Plesk se encarga de crear y gestionar toda la pila out-of-the-box.

docker remoto con panel

Servicios Docker remotos y cambio de contexto con Plesk

Plesk no solo arranca contenedores en el propio servidor: también es capaz de conectarse a motores Docker remotos. Para ello hay que configurar primero el daemon remoto según la documentación oficial de Docker, abriendo la API TCP y protegiéndola con certificados TLS.

El proceso típico en el host remoto pasa por crear un archivo /etc/docker/daemon.json con la configuración adecuada, generar certificados .pem para el servidor y el cliente, y modificar el servicio de Docker para que arranque tras tener la configuración de red lista. Después exportas los certificados al equipo donde corre Plesk.

En el servidor con Plesk, vas a Docker > Entornos y añades un nuevo servidor remoto introduciendo los datos de conexión y certificados. Puedes marcarlo como activo para que todas las operaciones Docker del panel se realicen sobre ese nodo. Cambiar de un servicio Docker a otro es tan simple como elegirlo en la lista de entornos y pulsar “Definir activo”.

Desde la sección de imágenes, Plesk te deja ver todas las imágenes locales de ese Docker remoto, filtrarlas, ver las etiquetas disponibles, el espacio de disco ocupado y eliminar las que no necesites con un clic. Es una forma cómoda de hacer limpieza sin tener que recordar todas las opciones de docker image prune y compañía.

Desarrollo con Docker Desktop, WSL 2 y contenedores remotos

En entornos Windows modernos es muy frecuente usar Docker Desktop con WSL 2 para tener un motor Docker basado en Linux totalmente integrado. Docker Desktop permite ejecutar contenedores Windows y Linux en la misma máquina, y habilitar la integración con las distintas distribuciones WSL que tengas instaladas (Ubuntu, Debian, etc.).

Tras instalar WSL 2 y Docker Desktop, en la configuración activas el motor basado en WSL 2 y eliges qué distribuciones tienen integración Docker. Desde una terminal WSL puedes comprobar la versión con docker --version y probar que todo funciona lanzando la imagen de prueba:

docker run hello-world

Este setup resulta ideal si quieres desarrollar en local usando VS Code y, a la vez, dirigir tus despliegues hacia servidores remotos. Gracias a las extensiones WSL, Dev Containers y Docker de VS Code puedes abrir tu proyecto dentro de un contenedor de desarrollo, compilar imágenes, depurar y luego empujar esas imágenes o archivos Compose hacia el servidor remoto donde se ejecutarán en producción.

VS Code y contenedores de desarrollo remotos

Con VS Code y la extensión Dev Containers puedes convertir cualquier proyecto en un entorno de desarrollo en contenedor. El flujo habitual es: clonar el repo (en tu distro WSL, por ejemplo), abrir la carpeta con code . y elegir “Reabrir en contenedor” para que VS Code genere una carpeta .devcontainer con su Dockerfile y devcontainer.json.

Cuando el contenedor de desarrollo arranca, VS Code se conecta a él y todo el tooling (intellisense, depuración, terminal integrado) se ejecuta dentro de ese contenedor. Puedes comprobarlo con uname para ver que sigues en Linux y con python3 --version o el runtime que uses para comprobar versiones específicas del contenedor.

Desde ahí puedes configurar perfiles de ejecución y depuración (por ejemplo, para un proyecto Django, Node o cualquier framework) mediante un launch.json en la carpeta .vscode. Al pulsar F5, la app se levanta dentro del contenedor de desarrollo y la ves en tu navegador, pero ese mismo código y Dockerfile se pueden usar luego para generar la imagen que desplegarás en tu servidor remoto.

La ventaja de este enfoque es que minimizas el clásico “en mi máquina funciona”: si desarrollas dentro de un contenedor y luego despliegas la misma imagen (o una muy similar) en el servidor, las diferencias de entorno se reducen al mínimo.

Contenedores remotos para aplicaciones gráficas (noVNC, TigerVNC, Caddy)

Hasta ahora hemos hablado de servicios típicos de backend o web, pero también es posible ejecutar aplicaciones de escritorio completas dentro de un contenedor y acceder a ellas desde un navegador en cualquier dispositivo. Un ejemplo muy ilustrativo es empaquetar Mozilla Thunderbird dentro de un contenedor que combina TigerVNC, noVNC y el servidor web Caddy.

La idea es que el contenedor incluya un servidor X11/VNC (TigerVNC), un servidor noVNC para exponer la sesión VNC como WebSocket y un gestor de procesos como supervisord que arranque y supervise todos los componentes: el servidor gráfico, el servidor noVNC, el gestor de ventanas (OpenBox) y la aplicación principal (Thunderbird).

En la configuración de supervisord.conf defines primero el bloque global del demonio (para que se ejecute en primer plano y saque logs a stdout) y luego programas individuales: uno para el servidor X11 con Xtigervnc, otro para easy-novnc escuchando en el puerto 8080 y conectando al VNC en 5900, otro para OpenBox como window manager y un último bloque para lanzar /usr/bin/thunderbird con la variable DISPLAY apuntando al display :0.

  Hostgator. Planes, Precios, Alternativas, Ventajas

OpenBox usa su propio menú raíz definido en menu.xml, donde creas entradas para abrir Thunderbird, un terminal y htop. Este menú se muestra al hacer clic derecho sobre el escritorio dentro de la sesión gráfica accesible vía navegador.

Construir la imagen: Dockerfile multietapa y aplicación gráfica

Para que todo encaje, se usa un Dockerfile multietapa. En la primera fase, basada en golang:1.14-buster, se compila el binario easy-novnc desde el repositorio de GitHub fijando una versión concreta para garantizar builds deterministas. En la segunda fase se parte de debian:buster e instalas los paquetes necesarios: openbox, tigervnc-standalone-server, supervisor, gosu y diversas utilidades (terminal, nano, wget, openssh-client, rsync, ca-certificates, xdg-utils, htop, herramientas de compresión, etc.).

Después instalas Thunderbird como aplicación principal, copias el binario easy-novnc desde la primera etapa al PATH y añades los archivos de configuración menu.xml y supervisord.conf al sistema de ficheros de la imagen. Se expone el puerto 8080, que será el punto de entrada HTTP para la sesión remota.

Para no ejecutar todo como root, el Dockerfile crea un usuario app con UID y GID 1000, prepara el directorio /data, lo marca como VOLUME para persistir la configuración de la aplicación y define como comando de arranque un CMD que ajusta permisos sobre /data y /dev/stdout y luego ejecuta supervisord como el usuario app mediante gosu.

Con esto construido, solo te queda crear la imagen con docker build -t thunderbird ., una red docker thunderbird-net, un volumen thunderbird-data y lanzar el contenedor con una política de reinicio siempre activa, montando el volumen en /data y conectándolo a esa red. El contenedor thunderbird-app se queda así listo y arrancado en segundo plano.

Proteger el acceso y exponer archivos con Caddy y WebDAV

Para no dejar la interfaz de noVNC expuesta sin ningún tipo de control, se crea otro contenedor basado en Caddy v2 con módulo WebDAV. Este segundo contenedor hace de reverse proxy hacia thunderbird-app:8080, pide autenticación básica y, además, sirve el contenido de /data por HTTP y WebDAV para poder acceder a los archivos de la aplicación.

El Dockerfile de Caddy también es multietapa: primero compila Caddy con el módulo caddy-webdav y después construye una imagen minimalista basada en Debian que solo incluye gosu y el binario de Caddy. Se copia un Caddyfile a /etc/Caddyfile, se expone el puerto 8080 y se crea un usuario app similar al del contenedor anterior, compartiendo también un volumen /data.

El Caddyfile define un servidor en el puerto 8080 que hace proxy de la raíz hacia thunderbird-app:8080, expone un navegador de archivos en /files y un endpoint WebDAV en /webdav, ambos usando el mismo directorio de datos compartido. La autenticación HTTP básica usa un usuario y una contraseña en hash leídos de las variables de entorno APP_USERNAME y APP_PASSWORD_HASH.

Para generar el hash de la contraseña se ejecuta un contenedor temporal de thunderbird-caddy con el comando caddy hash-password -plaintext 'mypass', se copia la salida y luego se lanza el contenedor final thunderbird-web con docker run –env APP_USERNAME=»myuser» –env APP_PASSWORD_HASH=»mypass-hash», publicando el puerto 8080 en el host.

Acceso desde el navegador y gestión de archivos

Con ambos contenedores en marcha, ya puedes ir en tu navegador a http://IP_DEL_SERVIDOR:8080, introducir usuario y contraseña y pulsar en “Connect” en la interfaz de noVNC. Verás un escritorio negro con el gestor de ventanas OpenBox; un clic derecho muestra el menú con Thunderbird, Terminal y Htop.

La ventana de la aplicación se adapta automáticamente al tamaño del navegador gracias a la configuración de resize remoto de TigerVNC y easy-novnc. El rendimiento suele ser más que aceptable incluso en dispositivos modesto como Chromebooks, porque el render se hace directamente en el servidor, no en tu cliente.

Si visitas http://IP_DEL_SERVIDOR:8080/files/ verás un listado de archivos del directorio de datos, y si montas http://IP_DEL_SERVIDOR:8080/webdav/ en un cliente WebDAV compatible podrás leer y escribir directamente en esa carpeta. En Windows, para mapearlo como unidad de red necesitarás añadir HTTPS mediante un proxy inverso externo o ajustar el registro para permitir auth básica en HTTP.

La gracia de este montaje es que puedes repetir el patrón con cualquier aplicación gráfica Linux: GIMP, entornos de ingeniería inversa como Cutter o incluso Wine para ejecutar aplicaciones Windows dentro de un contenedor Linux con acceso remoto. Todo ello apoyándote en la potencia de un servidor remoto mientras desde tu equipo solo necesitas un navegador.

Con todo lo anterior, el panorama para desplegar contenedores Docker en servidores remotos queda bastante claro: puedes tirar de SSH y Docker Compose simples, apoyarte en paneles como Plesk o Portainer, usar flujos de CI/CD con GitHub Actions, o incluso encapsular aplicaciones gráficas completas accesibles por web, todo ello descargando el peso del cómputo en máquinas remotas y dejando tu equipo local libre para lo que mejor sabe hacer: desarrollar y orquestar, sin achicharrar sus recursos.