Execute o Docker Compose como um Serviço Linux com systemd

Docker Compose no boot, gerenciado pelo systemd.

Conteúdo da página

O Docker Compose em um servidor Linux deve iniciar na boot, parar limpaamente durante o desligamento e sobreviver a reinicializações sem intervenção manual.

Docker Compose não é Kubernetes, e isso é aceitável para as cargas de trabalho que este guia aborda. Para muitos sistemas reais, um projeto Compose em um único host Linux é a quantidade certa de infraestrutura: simples, legível, fácil de fazer backup e suficiente para ferramentas internas, projetos paralelos, serviços auto-hospedados, ambientes de staging, pequenos aplicativos de produção e infraestrutura de desenvolvedores.

docker compose config ont the table with laptop

A peça que geralmente falta é o gerenciamento de serviços. Executar isso manualmente não é suficiente:

docker compose up -d

Um único comando inicia a stack, mas não documenta como a stack deve iniciar na boot, parar durante o desligamento, recarregar após mudanças, escrever logs, se recuperar de falhas ou ser atualizada com segurança. É aí que o systemd ajuda.

Este guia percorre a execução de um projeto Docker Compose como um serviço Linux com o systemd — arquivos de unidade, ordem de boot, atualizações, logs e backups. A divisão de responsabilidade é intencional: o Docker executa os containers, o Compose define a stack e o systemd inicia e para o projeto no host. Faz parte de Ferramentas de Desenvolvedor - Um Guia para Fluxos de Trabalho de Desenvolvimento.

Quando Faz Sentido Usar Docker Compose como Serviço

Executar o Compose sob o systemd faz sentido quando você tem:

  • Um servidor Linux único
  • Um pequeno aplicativo auto-hospedado
  • Uma stack de proxy reverso
  • Uma stack de monitoramento
  • Uma plataforma de desenvolvimento local
  • Uma ferramenta interna
  • Um ambiente de staging
  • Um serviço de produção simples com limites conhecidos

Exemplos:

  • Nginx Proxy Manager
  • Traefik
  • Gitea
  • Grafana e Prometheus
  • PostgreSQL mais um pequeno aplicativo web
  • Uptime Kuma
  • Serviços auxiliares do Home Assistant
  • Registro privado
  • API interna mais worker mais Redis

O Compose é uma boa opção quando o modelo operacional ainda é compreensível por uma pessoa lendo um único diretório.

Quando o Docker Compose Não é Suficiente

Use outra coisa quando você precisar de:

  • Agendamento em múltiplos nós
  • Reagendamento automático entre hosts
  • Descoberta de serviços em nível de cluster
  • Escala horizontal automática
  • Implantações rolling em muitas máquinas
  • Identidade de carga de trabalho granular
  • Política de rede complexa
  • Operações de plataforma multi-times em larga escala

Nesse ponto, Kubernetes, Nomad, Swarm ou uma plataforma gerenciada podem ser uma melhor opção.

Minha regra prática é evitar usar Kubernetes apenas para não precisar aprender systemd, e evitar usar Compose quando a carga de trabalho claramente precisa de orquestração em múltiplos hosts.

A Arquitetura Básica

Uma configuração limpa separa os arquivos do projeto, a unidade systemd e os dados persistentes no host. O projeto Compose fica em /opt/myapp/ com compose.yaml, .env, data/, backups/ e scripts opcionais como scripts/update.sh. O arquivo de unidade systemd fica em /etc/systemd/system/myapp.service.

flowchart TB subgraph host["Linux host"] systemd["systemd unit\n/etc/systemd/system/myapp.service"] compose["Docker Compose\n/opt/myapp/compose.yaml"] docker["Docker Engine"] fs["Persistent data\n/opt/myapp/data/"] end systemd -->|"ExecStart: docker compose up -d"| compose compose --> docker docker --> fs

Cada camada tem uma tarefa clara: o Docker executa os containers, o Compose define a stack de aplicativos, o systemd inicia e para o projeto Compose na boot e no desligamento, o sistema de arquivos do host armazena dados persistentes, os backups permanecem explícitos e as atualizações passam por etapas scriptadas e revisáveis. Este layout é deliberadamente entediante, porque infraestrutura entediante é mais fácil de consertar quando algo quebra às 2 da manhã.

Preparar o Diretório do Projeto Compose

Crie um diretório em /opt:

sudo mkdir -p /opt/myapp
sudo chown -R "$USER":"$USER" /opt/myapp
cd /opt/myapp

Crie um arquivo Compose:

nano compose.yaml

Exemplo:

services:
  web:
    image: nginx:stable
    restart: unless-stopped
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html:ro
    healthcheck:
      test: ["CMD-SHELL", "nginx -t || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

volumes: {}

Crie o diretório de conteúdo:

mkdir -p html
echo "Hello from Docker Compose" > html/index.html

Teste manualmente primeiro:

docker compose up -d
docker compose ps
docker compose logs --tail=50

Em seguida, pare-o antes de entregar o ciclo de vida ao systemd:

docker compose down

Não crie um serviço systemd até que o projeto Compose funcione manualmente. Enquanto você testa, mantenha o Cheat Sheet do Docker Compose por perto para ps, logs, pull e estrutura do projeto.

Usar o Comando Moderno docker compose

O Docker Engine e o plugin Compose devem estar instalados antes que você escreva um arquivo de unidade. No Ubuntu, Instalar Docker no Ubuntu percorre APT, Snap, modo rootless e segurança pós-instalação para que você termine com um comando docker compose funcional.

Use isto:

docker compose version

Não isto:

docker-compose version

O antigo binário docker-compose ainda existe em muitas máquinas, mas o Docker moderno usa o Compose como um plugin do Docker CLI.

Em arquivos de serviço e scripts, prefira:

/usr/bin/docker compose

Você pode encontrar o caminho do Docker com:

command -v docker

Geralmente é:

/usr/bin/docker

Criar um Serviço systemd para Docker Compose

Se arquivos de unidade são novos para você, Executar qualquer Executável como Serviço no Linux explica Type, ExecStart, systemctl e o fluxo de trabalho geral do systemd. Esta seção aplica esses padrões especificamente a uma stack Compose.

Crie o arquivo de serviço:

sudo nano /etc/systemd/system/myapp.service

Use esta unidade:

[Unit]
Description=MyApp Docker Compose stack
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target

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

[Install]
WantedBy=multi-user.target

Recarregue o systemd:

sudo systemctl daemon-reload

Inicie o serviço:

sudo systemctl start myapp.service

Ative-o na boot:

sudo systemctl enable myapp.service

Verifique o status:

systemctl status myapp.service

Verifique os containers:

cd /opt/myapp
docker compose ps

Por que Type=oneshot e RemainAfterExit=yes?

Esta é a parte que muitos guias erram sutilmente.

docker compose up -d inicia os containers em modo detached e sai, então não há um processo Compose em primeiro plano de longa execução para o systemd supervisionar. A unidade systemd não deve fingir que docker compose up -d é um daemon de longa execução.

Use:

Type=oneshot
RemainAfterExit=yes

Isto diz ao systemd:

  • Execute o comando de início.
  • Considere a unidade ativa após o comando sair com sucesso.
  • Execute ExecStop quando o serviço for parado.

Isso corresponde ao comportamento real do Compose detached, é por isso que Type=oneshot com RemainAfterExit=yes é o padrão correto para a maioria das stacks.

Por que não Type=simple?

Com Type=simple, o systemd espera que o processo ExecStart continue rodando, mas docker compose up -d sai após iniciar os containers. Isso pode fazer o systemd achar que o serviço terminou, então chamar a lógica de parada ou marcar a unidade como inativa dependendo da configuração.

Se você quiser Type=simple, você geralmente executaria o Compose em primeiro plano:

ExecStart=/usr/bin/docker compose up

Isso pode funcionar, mas geralmente não prefiro isso para stacks Compose em servidores. Containers detached mais ExecStop explícito são mais fáceis de operar.

Uma Unidade Mais Amigável para Produção

Para um servidor real, prefiro uma unidade um pouco mais estrita:

[Unit]
Description=MyApp Docker Compose stack
Documentation=https://example.com/docs/myapp
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/myapp
EnvironmentFile=-/opt/myapp/.env.systemd
ExecStartPre=/usr/bin/docker compose config --quiet
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecReload=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0
TimeoutStopSec=120

[Install]
WantedBy=multi-user.target

Detalhes importantes:

  • WorkingDirectory aponta para o projeto Compose.
  • ExecStartPre valida a configuração do Compose.
  • ExecReload recria os serviços alterados.
  • ExecStop para e remove os containers do projeto Compose e a rede padrão.
  • EnvironmentFile=-... significa que o arquivo é opcional.

Crie o arquivo de ambiente systemd opcional:

nano /opt/myapp/.env.systemd

Exemplo:

COMPOSE_PROJECT_NAME=myapp

Em seguida, recarregue o systemd:

sudo systemctl daemon-reload
sudo systemctl restart myapp.service

Compose .env vs EnvironmentFile do systemd

Compose e systemd cada um tem seu próprio mecanismo de ambiente, e misturá-los causa falhas confusas de “variável não definida” na boot.

O Compose lê automaticamente um arquivo .env no diretório do projeto para substituição de variáveis no arquivo Compose.

Exemplo .env:

APP_TAG=1.2.3
WEB_PORT=8080

Exemplo compose.yaml:

services:
  web:
    image: nginx:${APP_TAG}
    ports:
      - "${WEB_PORT}:80"

Um EnvironmentFile do systemd define variáveis de ambiente para o próprio comando docker compose.

Exemplo:

EnvironmentFile=-/opt/myapp/.env.systemd

Para muitos projetos, você só precisa do .env do Compose.

Use um arquivo de ambiente systemd quando quiser definir coisas como:

COMPOSE_PROJECT_NAME=myapp
COMPOSE_FILE=compose.yaml
DOCKER_HOST=unix:///var/run/docker.sock

Não use nenhum dos arquivos como um cofre de segredos casual. Se segredos importam, use segredos do Docker, um gerenciador de segredos externo, arquivos criptografados ou, pelo menos, permissões estritas.

Defina permissões restritivas:

chmod 600 /opt/myapp/.env
chmod 600 /opt/myapp/.env.systemd

Políticas de Reinício: Docker vs systemd

Existem duas camadas de reinício — política de reinício do container no Compose e política de reinício do serviço systemd — e elas não devem ser misturadas cegamente.

Para containers de longa execução, defina políticas de reinício no Compose:

services:
  web:
    image: nginx:stable
    restart: unless-stopped

Valores comuns de reinício:

Política Significado
no Não reiniciar automaticamente
always Reiniciar após saída e reinício do daemon
on-failure Reiniciar apenas após falha
unless-stopped Reiniciar a menos que seja parado manualmente

Para a maioria dos serviços persistentes, prefiro:

restart: unless-stopped

É previsível e respeita paradas manuais intencionais.

A própria unidade systemd geralmente não deve reiniciar repetidamente, porque docker compose up -d não é a carga de trabalho em execução. Os containers são.

Então evite isto a menos que você tenha um motivo específico:

Restart=always

Na maioria das unidades Compose-como-serviço, deixe o Docker lidar com os reinícios dos containers.

Verificações de Saúde (Health Checks)

Políticas de reinício reiniciam containers quando processos saem. Elas não consertam magicamente todos os aplicativos não saudáveis.

Adicione verificações de saúde onde forem úteis:

services:
  app:
    image: example/app:latest
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS http://localhost:8080/health || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 20s

Verifique a saúde:

docker compose ps

Inspecione um container:

docker inspect container-name

Verificações de saúde são especialmente úteis para:

  • Aplicativos web
  • Proxies reversos
  • Bancos de dados
  • Filas
  • APIs internas
  • Workers com um endpoint de saúde

Elas são menos úteis quando verificam apenas que um processo existe, porque um processo que está vivo mas travado ainda parece saudável. Uma verificação de saúde ruim é apenas outra mentira em YAML.

Ordem de Inicialização e depends_on

O Compose pode definir dependências:

services:
  app:
    image: example/app:latest
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

Isso pode ajudar na ordem de inicialização, mas não confie demais nisso. Os aplicativos ainda devem lidar com retries — bancos de dados reiniciam, redes falham, DNS leva tempo, e um aplicativo resiliente retry connections em vez de assumir uma ordem de inicialização perfeita.

Logs: journalctl e docker compose logs

Duas visualizações de logs cobrem a maioria das depurações: o systemd captura o ciclo de vida da unidade em si, enquanto o Compose captura a saída do aplicativo dos containers em execução.

Logs do serviço systemd:

journalctl -u myapp.service -n 100 --no-pager

Acompanhe logs do systemd:

journalctl -u myapp.service -f

Logs do serviço Compose:

cd /opt/myapp
docker compose logs --tail=100
docker compose logs -f
docker compose logs -f web

Para a maioria das depurações de aplicativos, docker compose logs é mais útil; para depuração de ciclo de vida — falhas de início, crashes de unidade, erros de permissão — journalctl é mais útil. Se systemctl start myapp falhar, verifique journalctl primeiro. Se a stack iniciar mas o aplicativo estiver quebrado, verifique docker compose logs.

Rotação de Logs

Logs do Docker podem crescer para sempre se você não configurá-los.

Para servidores pequenos, configure a rotação de logs do Docker em /etc/docker/daemon.json:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "5"
  }
}

Reinicie o Docker:

sudo systemctl restart docker

Em seguida, reinicie a stack Compose:

sudo systemctl restart myapp.service

Isso se aplica a containers recém-criados. Recrie containers se necessário:

cd /opt/myapp
docker compose up -d --force-recreate

Rotação de logs não é glamorosa, mas é uma das maneiras mais fáceis de evitar uma interrupção de disco cheio em um servidor pequeno.

Atualizando um Serviço Compose

Um fluxo de atualização manual simples:

cd /opt/myapp
docker compose pull
docker compose up -d --remove-orphans
docker image prune -f

Se gerenciado pelo systemd, você pode usar:

sudo systemctl reload myapp.service

Se sua unidade tiver:

ExecReload=/usr/bin/docker compose up -d --remove-orphans

Mas note: ExecReload não puxa imagens a menos que você inclua essa etapa.

Para atualizações explícitas, crie um script.

mkdir -p /opt/myapp/scripts
nano /opt/myapp/scripts/update.sh

Script:

#!/usr/bin/env bash
set -euo pipefail

cd /opt/myapp

docker compose config --quiet
docker compose pull
docker compose up -d --remove-orphans
docker image prune -f
docker compose ps

Torne-o executável:

chmod +x /opt/myapp/scripts/update.sh

Execute-o:

/opt/myapp/scripts/update.sh

Então a unidade de serviço pode permanecer focada no ciclo de vida, enquanto o script de atualização lida com a implantação.

Script de Atualização Mais Seguro com Hook de Backup

Para serviços com estado, atualize apenas após backup.

#!/usr/bin/env bash
set -euo pipefail

APP_DIR="/opt/myapp"
BACKUP_DIR="/opt/myapp/backups"

cd "$APP_DIR"

mkdir -p "$BACKUP_DIR"

echo "Validando arquivo compose"
docker compose config --quiet

echo "Executando hook de backup"
if [ -x "$APP_DIR/scripts/backup.sh" ]; then
  "$APP_DIR/scripts/backup.sh"
else
  echo "Nenhum hook de backup encontrado"
fi

echo "Puxando imagens"
docker compose pull

echo "Recriando serviços"
docker compose up -d --remove-orphans

echo "Limpando imagens não utilizadas"
docker image prune -f

echo "Status atual"
docker compose ps

Isso ainda é simples, mas agora codifica um hábito operacional: backup antes da mudança.

Parando o Serviço

Pare a stack:

sudo systemctl stop myapp.service

Isso executa:

docker compose down

Por padrão, docker compose down remove:

  • Containers para serviços no arquivo Compose
  • Redes definidas pelo arquivo Compose
  • A rede padrão

Ele não remove volumes nomeados a menos que você peça.

Não use casualmente:

docker compose down -v

Isso remove volumes nomeados declarados no arquivo Compose e volumes anônimos anexados aos containers. Para bancos de dados e aplicativos com estado, isso pode significar excluir dados reais.

Use down -v apenas quando você significa “destruir este ambiente”.

Reiniciando o Serviço

Reinicie a unidade systemd:

sudo systemctl restart myapp.service

Isso executa o comando de parada e então o comando de início.

Para apenas reiniciar containers sem recriá-los:

cd /opt/myapp
docker compose restart

Distinção importante:

  • docker compose restart reinicia containers existentes.
  • docker compose up -d aplica mudanças de configuração ou imagem recriando containers quando necessário.

Se você mudou compose.yaml, use:

docker compose up -d

Não apenas:

docker compose restart

Lidando com Containers Órfãos

Se você renomear ou remover um serviço em compose.yaml, containers antigos podem permanecer como órfãos.

Use:

docker compose up -d --remove-orphans

É por isso que os exemplos de serviço systemd neste guia usam:

ExecStart=/usr/bin/docker compose up -d --remove-orphans

Isso mantém a stack mais próxima do arquivo Compose atual.

Backups

Backups dependem da carga de trabalho, mas os princípios são estáveis.

Para bind mounts:

/opt/myapp/data/

Faça backup desse diretório.

Para volumes nomeados:

docker volume ls

Inspecione um volume:

docker volume inspect volume-name

Para bancos de dados, cópias de sistema de arquivos nem sempre são suficientes. Use backups conscientes do aplicativo:

Exemplo PostgreSQL:

docker compose exec -T db pg_dump -U postgres appdb > backups/appdb.sql

Exemplo MariaDB:

docker compose exec -T db mariadb-dump -u root -p appdb > backups/appdb.sql

Exemplo Redis:

docker compose exec redis redis-cli BGSAVE

Uma stack Compose sem um plano de backup não é um serviço — é um experimento temporário que acontece ter uptime.

Linha de Base de Segurança

Para um pequeno serviço Compose no Linux, comece com esta linha de base:

  • Mantenha o projeto Compose em /opt/appname.
  • Use tags de imagem explícitas, não apenas latest, quando a estabilidade importa.
  • Use bind mounts ou volumes nomeados deliberadamente.
  • Não exponha portas que você não precisa.
  • Coloque serviços públicos atrás de um proxy reverso.
  • Use HTTPS na borda.
  • Mantenha segredos fora do Git.
  • Restrinja permissões de .env.
  • Evite containers privilegiados a menos que realmente necessário.
  • Evite montar o socket do Docker em containers.
  • Mantenha Docker e imagens atualizados.
  • Teste o comportamento do firewall de outra máquina.

Um padrão perigoso:

volumes:
  - /var/run/docker.sock:/var/run/docker.sock

Isso dá ao container controle sobre o Docker. Na prática, isso pode se tornar controle em nível de host. Use-o apenas quando você entende o risco.

Limites de Recursos

Em servidores pequenos, um container ruim pode consumir o host.

O Compose suporta configurações relacionadas a recursos, mas o comportamento pode depender da versão do Docker Engine e do Compose. Para proteção simples, comece com limites em nível de aplicativo e limites de log do Docker.

Para algumas cargas de trabalho, você pode adicionar limites de memória:

services:
  app:
    image: example/app:stable
    restart: unless-stopped
    mem_limit: 512m

Também configure contagens de workers em nível de aplicativo, limites de fila e tamanhos de cache. Limites de container são úteis, mas não são um substituto para entender o aplicativo.

Exemplo: Um Serviço Compose Realista

Diretório:

/opt/whoami/
  compose.yaml
  .env

Arquivo Compose:

services:
  whoami:
    image: traefik/whoami:v1.10
    restart: unless-stopped
    ports:
      - "${WHOAMI_PORT}:80"
    healthcheck:
      test: ["CMD-SHELL", "wget -qO- http://localhost || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3

Arquivo .env:

WHOAMI_PORT=8080
COMPOSE_PROJECT_NAME=whoami

Unidade systemd:

[Unit]
Description=Whoami Docker Compose stack
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/whoami
ExecStartPre=/usr/bin/docker compose config --quiet
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecReload=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0
TimeoutStopSec=120

[Install]
WantedBy=multi-user.target

Instale-o:

sudo systemctl daemon-reload
sudo systemctl enable --now whoami.service

Teste:

curl http://localhost:8080

Verifique o status:

systemctl status whoami.service
cd /opt/whoami
docker compose ps

Solução de Problemas

Serviço Inicia mas Containers Não Estão Rodando

Verifique o systemd:

journalctl -u myapp.service -n 100 --no-pager

Valide o Compose:

cd /opt/myapp
docker compose config

Verifique o Docker:

systemctl status docker
docker info

WorkingDirectory Está Errado

Se o systemd não conseguir encontrar seu arquivo Compose, confirme:

WorkingDirectory=/opt/myapp

Em seguida, verifique:

ls -la /opt/myapp
ls -la /opt/myapp/compose.yaml

O serviço roda a partir de WorkingDirectory, não do diretório atual do seu shell.

Permissão Negada do Docker

Se a unidade roda como root, ela normalmente pode acessar o Docker.

Se você definir User=algumusuario, esse usuário deve ser capaz de acessar o Docker. Geralmente isso significa membro do grupo docker, ou uma configuração Docker rootless.

Verifique:

groups algumusuario

Adicione o usuário se apropriado:

sudo usermod -aG docker algumusuario

Tenha cuidado. O grupo Docker é efetivamente privilegiado.

Comando Compose Não Encontrado

Encontre o Docker:

command -v docker

Use o caminho completo na unidade:

ExecStart=/usr/bin/docker compose up -d --remove-orphans

Se o plugin Compose estiver faltando:

docker compose version

Instale-o usando sua fonte de pacote Docker.

Variáveis de Ambiente Estão Faltando

Verifique a configuração do Compose como o systemd veria:

cd /opt/myapp
docker compose config

Se o systemd precisar de variáveis de ambiente extras, use:

EnvironmentFile=-/opt/myapp/.env.systemd

Se o Compose precisar de variáveis para substituição, use:

/opt/myapp/.env

Estes estão relacionados, mas não são idênticos.

Containers Não Iniciam Após Reinicialização

Verifique se o serviço systemd está habilitado:

systemctl is-enabled myapp.service

Habilite-o:

sudo systemctl enable myapp.service

Verifique o Docker:

systemctl is-enabled docker
systemctl status docker

Verifique logs de boot:

journalctl -u myapp.service -b --no-pager

Aplicativo Inicia Antes do Banco de Dados Estar Pronto

Adicione uma verificação de saúde do banco de dados e depends_on com service_healthy.

Também conserte o aplicativo. Ele deve fazer retry de conexões de banco de dados. A ordenação de início de infraestrutura é útil, mas a lógica de retry do aplicativo é melhor.

Disco Cheio com Logs do Docker

Verifique o uso de disco do Docker:

docker system df

Verifique logs grandes de containers:

sudo du -h /var/lib/docker/containers | sort -h | tail

Configure a rotação de logs do Docker em /etc/docker/daemon.json.

Em seguida, recrie os containers.

Erros Comuns

Erro 1: Executando docker compose up no rc.local

Executar docker compose up de rc.local ou um script de login funciona até que não funcione — use uma unidade systemd adequada em vez disso.

Erro 2: Usando Restart=always no systemd e restart: always no Compose

Geralmente você só precisa de políticas de reinício de container no Compose. Evite dois supervisores lutando entre si.

Erro 3: Esquecendo –remove-orphans

Renomeações e remoções de serviços podem deixar containers antigos para trás. Use:

docker compose up -d --remove-orphans

Erro 4: Usando docker compose restart Após Mudanças de Configuração

restart reinicia containers. Ele não aplica todas as mudanças de configuração.

Use:

docker compose up -d

Erro 5: Executando down -v Sem Pensar

Isso pode excluir volumes. Para serviços com estado, isso pode significar excluir dados.

Erro 6: Sem Backup Antes do Pull

Novas imagens podem quebrar. Bancos de dados podem migrar. Tags podem mover. Faça backup primeiro.

Erro 7: Publicando Toda Porta

Publique apenas o que o host precisa expor. Tráfego interno de serviço para serviço pode permanecer na rede Compose.

Padrão Recomendado Final

Para a maioria dos serviços Linux de host único, use este padrão:

Arquivo Compose:

services:
  app:
    image: example/app:stable
    restart: unless-stopped
    ports:
      - "8080:8080"
    env_file:
      - .env

Unidade systemd:

[Unit]
Description=MyApp Docker Compose stack
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/myapp
ExecStartPre=/usr/bin/docker compose config --quiet
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecReload=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0
TimeoutStopSec=120

[Install]
WantedBy=multi-user.target

Habilite-o:

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

Opere-o:

sudo systemctl status myapp.service
sudo systemctl restart myapp.service
journalctl -u myapp.service -f
cd /opt/myapp && docker compose logs -f

Este padrão não é sofisticado, e esse é o ponto. Docker Compose é excelente para sistemas pequenos e compreensíveis, systemd é excelente para iniciar e parar serviços de host, e juntos eles dão a você um modelo de implantação de servidor único confiável sem fingir que cada projeto precisa de um cluster. Para comandos em nível de container fora do Compose — imagens, volumes, redes e limpeza — veja o Cheat Sheet do Docker.

Assinar

Receba novos artigos sobre sistemas, infraestrutura e engenharia de IA.