SSD Nodes Learn Hosting plans →
Guías Matt ConnorPor Matt Connor · Actualizado 2026-08-25

Docker Compose: iniciar servicios al arrancar

Configura Docker Compose para recuperar servicios tras reiniciar: usa restart: unless-stopped, entiende por qué on-failure no basta y cuándo necesitas systemd.

La respuesta corta

Los servicios de Docker Compose se inician durante el arranque cuando se cumplen dos condiciones al mismo tiempo. El daemon de Docker debe estar habilitado como servicio del sistema, y cada servicio del archivo debe tener una política de reinicio unless-stopped o always. Añada restart: unless-stopped a todos los servicios, ejecute docker compose up -d una vez y los contenedores volverán a iniciarse automáticamente después de un reinicio. En el caso habitual no hace falta nada más.

Sólo necesita una unidad de systemd cuando el orden de inicio es importante: por ejemplo, una pila que depende de un disco montado, una interfaz VPN o un recurso compartido de red que todavía no está disponible cuando se inicia el daemon de Docker. Este caso es real y se explica en la segunda mitad de esta guía. Si todavía está familiarizándose con las definiciones de servicios y los volúmenes, empiece por los conceptos básicos de Docker Compose en un VPS y vuelva después.

Establezca la política de reinicio en compose.yaml

La política se define con una línea por servicio. No existe un ajuste global. Por tanto, un servicio que olvide configurar permanecerá detenido después del reinicio, mientras el resto de la pila se inicia.

services:
  app:
    image: nginx:1.27
    restart: unless-stopped
    ports:
      - "8080:80"
  db:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_PASSWORD: change-me
    volumes:
      - dbdata:/var/lib/postgresql/data

volumes:
  dbdata:

Aplíquela y, después, lea la política del contenedor en ejecución:

docker compose up -d
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app)

Esto muestra unless-stopped. Si muestra no, se editó el archivo, pero el contenedor nunca se recreó.

Este es el fallo más habitual. La política de reinicio se almacena en el contenedor, no en el archivo YAML. Editar compose.yaml no cambia nada en un contenedor que ya existe. docker compose restart tampoco sirve, porque detiene e inicia el mismo objeto de contenedor sin modificar su configuración. Sólo docker compose up -d compara el archivo con los contenedores en ejecución, detecta que la política cambió y los recrea.

Para un contenedor que no quiera recrear ahora, cambie la política directamente:

docker update --restart unless-stopped my-container

Edite también el archivo YAML. docker update cambia el contenedor en ejecución, y el siguiente docker compose up -d leerá el archivo y restaurará el valor anterior.

Qué hace realmente cada valor de reinicio

Docker define cuatro valores. La diferencia entre ellos sólo se aprecia cuando se reinicia la máquina o el daemon.

  • no es el valor predeterminado. El contenedor nunca se reinicia automáticamente, bajo ninguna circunstancia.
  • always reinicia el contenedor cada vez que se detiene. Si lo detuvo manualmente, volverá a iniciarse la próxima vez que se inicie el daemon de Docker. Esto suele resultar inesperado: un contenedor que detuvo deliberadamente la semana pasada vuelve a estar en ejecución después de un reinicio.
  • unless-stopped se comporta como always, salvo que un contenedor detenido manualmente permanece detenido después de reiniciar el daemon. Este es el valor adecuado para un servicio que se detiene ocasionalmente por mantenimiento.
  • on-failure reinicia el contenedor sólo cuando termina con un código de salida distinto de cero. Puede limitar los intentos, como en restart: on-failure:3.

Para un stack que simplemente debe estar activo cuando el servidor está activo, unless-stopped es el valor predeterminado adecuado. Elija always sólo cuando quiera evitar que un contenedor permanezca detenido.

Por qué restart: on-failure no sobrevive a un reinicio

Muchas personas eligen on-failure porque parece una opción prudente, pero después del primer reinicio encuentran todos los contenedores detenidos. El motivo está en la definición. on-failure reacciona a una sola cosa: que el proceso del contenedor termine con un código de error.

Un reinicio no es un error. Cuando el host se apaga, systemd detiene docker.service y el daemon detiene cada contenedor de forma deliberada. El contenedor no falló, por lo que la política no tiene nada a lo que reaccionar. Al volver a iniciar, el daemon comprueba qué contenedores debe reanudar. Un contenedor on-failure que se detuvo correctamente no está entre ellos. Permanece en el estado exited.

Puede comprobarlo directamente. Establezca restart: on-failure en un servicio, ejecute docker compose up -d, reinicie el sistema y después ejecute:

docker compose ps -a

El servicio aparece con el estado Exited y un estado similar a Exited (0) 2 minutes ago. No hay ningún problema ni se registra ningún error. Por eso este caso es difícil de diagnosticar. La política hizo exactamente lo que indica.

on-failure sigue siendo útil. Es adecuada para un contenedor que ejecuta un trabajo y puede fallar, cuando se quiere limitar el número de reintentos y evitar un bucle de reinicio. Es la opción incorrecta para mantener activo un servicio de ejecución prolongada después de los reinicios.

Las políticas de reinicio sólo funcionan si el servicio Docker se inicia durante el arranque

Docker daemon aplica las políticas de reinicio. Si el daemon no se inicia, no se aplica ninguna política. Compruébelo:

systemctl is-enabled docker
systemctl is-enabled containerd

Ambos comandos deben mostrar enabled. Los paquetes del repositorio oficial de Docker las habilitan durante la instalación, por lo que esto suele funcionar en un servidor recién instalado. Si alguno muestra disabled, corríjalo:

sudo systemctl enable --now docker containerd

Aquí hay un detalle importante. Ubuntu también incluye docker.socket, que inicia el daemon bajo demanda la primera vez que algo se comunica con la API de Docker. Al ver docker.socket habilitado, algunos administradores dan por hecho que el daemon está cubierto y deshabilitan docker.service para ahorrar memoria. Durante el arranque, ningún proceso llama a la API, por lo que el socket nunca recibe conexiones, el daemon no se inicia y ningún contenedor arranca hasta que se ejecuta el primer comando docker. La activación mediante socket no sustituye a tener docker.service habilitado.

Cuándo una unidad de systemd es la mejor opción

Las políticas de reinicio no pueden establecer un orden respecto al resto del sistema. El daemon se inicia y levanta los contenedores en cuanto puede. Si la pila monta mediante bind una carpeta de un volumen independiente, un recurso compartido NFS (sistema de archivos de red) o un disco cifrado, los contenedores pueden iniciarse antes de que exista esa ruta. Docker creará sin problemas una carpeta vacía en el punto de montaje e iniciará el contenedor usándola, y la base de datos arrancará sin datos.

Escriba una unidad de systemd cuando se cumpla cualquiera de estas condiciones. La pila necesita que primero esté listo un montaje, una interfaz VPN u otra unidad. Quiere que systemctl stop myapp y systemctl start myapp funcionen como en cualquier otro servicio del sistema. O quiere que la pila se detenga correctamente durante el apagado, en lugar de terminar junto con el daemon. Si no conoce las unidades de systemd, crear un servicio y un temporizador de systemd explica con más detalle el formato del archivo.

Escritura de la unidad de systemd

Coloque la pila en una ruta fija fuera de un directorio personal. /srv/myapp es una buena opción, porque una unidad que se ejecuta antes de que alguien inicie sesión no tiene motivos para leer /home.

Cree /etc/systemd/system/myapp.service:

[Unit]
Description=myapp docker compose stack
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target
RequiresMountsFor=/srv/myapp/data

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/srv/myapp
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0

[Install]
WantedBy=multi-user.target

Habilítela e iníciela:

sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
systemctl status myapp.service

Una unidad en buen estado muestra Active: active (exited). La primera vez que lo vea, puede parecer incorrecto. Es correcto: Type=oneshot con RemainAfterExit=yes significa que la unidad ejecutó su comando, el comando terminó y systemd mantiene la unidad marcada como activa para que ExecStop se ejecute durante el apagado.

Cada línea tiene una función. Requires=docker.service hace que la unidad falle de inmediato en lugar de ejecutar docker compose contra un socket inactivo. After= establece el orden, porque Requires= por sí solo no lo hace. RequiresMountsFor= hace que systemd cargue la unidad de montaje de esa ruta y espere a que esté disponible. Esa es precisamente la razón para usar una unidad en lugar de una política de reinicio. TimeoutStartSec=0 impide que systemd finalice el trabajo de inicio mientras todavía se descarga una imagen grande.

Una nota sobre la combinación de ambos mecanismos. La documentación de Docker desaconseja mezclar las políticas de reinicio con un gestor de procesos del host. Esa advertencia se refiere a un gestor de procesos que supervisa directamente el proceso del contenedor y lo reinicia mientras el daemon intenta hacer lo mismo. Una unidad Type=oneshot no supervisa ningún proceso, por lo que es correcto mantener restart: unless-stopped en el archivo compose junto con esta unidad. De hecho, es lo que necesita. systemd gestiona el orden durante el arranque y el daemon gestiona un contenedor que falla a las tres de la madrugada.

La unidad tiene un aspecto diferente cuando lo que se mantiene activo es un proceso normal de larga duración y no una pila, porque en ese caso no hay ningún daemon subyacente y el propio Restart= de systemd debe encargarse de la supervisión; ejecutar dsh sin interfaz detrás de systemd es un ejemplo completo de este modelo, incluido el usuario dedicado y el journal.

Verifique tras un reinicio real

No hay sustituto para la prueba real. systemctl restart docker no comprueba el orden de montaje, y docker compose down seguido de docker compose up -d no comprueba nada relacionado con el arranque.

sudo reboot

Espere, vuelva a conectarse y compruebe lo siguiente en este orden:

uptime
systemctl is-active docker
docker compose ps

uptime confirma que está consultando una máquina que realmente se reinició. docker compose ps, ejecutado desde el directorio de la pila, debería mostrar todos los servicios como running con un tiempo de actividad aproximado al de la máquina. El servicio que muestre Exited es el que debe revisar.

Si algún servicio no se inició, el registro del daemon cubre el intervalo de arranque:

journalctl -u docker.service -b --no-pager | tail -50

Para una pila gestionada por una unidad, journalctl -u myapp.service -b --no-pager muestra la salida exacta de docker compose durante el arranque, incluido un error al descargar una imagen o la ausencia del archivo .env. El reinicio programado es el que debe supervisar, así que deje que la unidad le informe de los siguientes: una línea OnFailure= dirigida a un servidor ntfy autohospedado convierte una pila que no volvió a iniciarse en una notificación push, en lugar de algo que descubrirá días después.

Aspectos que interrumpen silenciosamente el inicio automático

Los contenedores creados con docker compose run nunca reciben la política de reinicio del archivo. Compose los trata como contenedores de un solo uso. Si un servicio parece ignorar su política, compruebe si se inició con run en lugar de up.

Una ruta relativa en un volumen o en una entrada env_file se resuelve respecto al directorio del archivo de Compose. Esto funciona desde el shell y también desde una unidad que establezca WorkingDirectory. Falla desde una unidad que no lo establezca, porque el directorio de trabajo es entonces /.

Rootless Docker es un caso aparte. El daemon se ejecuta como un servicio de usuario, y un servicio de usuario se detiene cuando termina la última sesión de ese usuario. Habilítelo para el usuario y permita que siga ejecutándose aunque no haya ninguna sesión iniciada:

systemctl --user enable docker
sudo loginctl enable-linger $USER

Sin enable-linger, el daemon rootless se apaga cuando cierra la sesión y los contenedores también se detienen. Esto parece exactamente una política de reinicio defectuosa.

Por último, las actualizaciones de seguridad automáticas pueden reiniciar un servidor a una hora fija. Esto sólo es útil si la pila se inicia de nuevo por sí sola. Configurarlo en una máquina nueva forma parte del trabajo de la primera hora, junto con los primeros diez minutos en un VPS nuevo.

FAQ

¿Cuál es la diferencia entre restart: always y restart: unless-stopped?

Ambas políticas reinician el contenedor cuando se detiene por sí solo. La diferencia aparece cuando detiene un contenedor manualmente. Con always, el contenedor vuelve a iniciarse la próxima vez que se inicia el daemon de Docker, por lo que un reinicio del sistema anula la detención manual. Con unless-stopped, el daemon recuerda que el contenedor se detuvo deliberadamente y lo deja detenido. Use unless-stopped salvo que necesite específicamente que el contenedor no permanezca detenido.

Añadí restart: unless-stopped, pero el contenedor sigue sin iniciarse después de un reinicio. ¿Por qué?

La política se almacena en el contenedor, no en el archivo, y editar el YAML no actualiza un contenedor que ya existe. Ejecute docker compose up -d para que Compose lo vuelva a crear y, después, confirme el resultado con docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app). Si muestra no, el contenedor se creó antes de la modificación. La otra causa habitual es que docker.service no esté habilitado. Puede comprobarlo con systemctl is-enabled docker.

¿Necesito una unidad de systemd si ya uso políticas de reinicio?

Normalmente no. Una política de reinicio es suficiente para un stack que sólo necesita la red, como ocurre con la mayoría de los stacks. Añada una unidad cuando los contenedores dependan de algo que no esté listo cuando se inicia el daemon de Docker, como un disco externo, un volumen cifrado, un recurso compartido NFS o una interfaz VPN. La unidad permite establecer el orden mediante After= y RequiresMountsFor=, algo que una política de reinicio no puede expresar.

¿Cómo detengo un stack de forma permanente sin que vuelva a iniciarse en el siguiente reinicio?

Con unless-stopped, basta con docker compose stop, porque un contenedor detenido manualmente no se reanuda cuando se reinicia el daemon. Con always, detenerlo no es suficiente y el contenedor vuelve a iniciarse después de un reinicio. Ejecute docker compose down para eliminar los contenedores o cambie antes la política con docker update --restart no my-container. Si una unidad de systemd administra el stack, ejecute también sudo systemctl disable myapp.service; de lo contrario, la unidad volverá a iniciarlo.