Docker em uma VPS: o que muda na prática
Docker em uma VPS mantém o mesmo engine, mas a RAM acaba, portas publicadas ignoram a UFW, containers não reiniciam e o disco pode lotar.
O que muda quando você executa Docker numa VPS
O Docker numa VPS usa o mesmo engine e as mesmas imagens que o Docker no seu portátil, por isso todos os comandos que já conhece continuam a funcionar. O que muda é o espaço disponível à volta dele. Um portátil tem memória disponível, uma firewall que ninguém está a analisar e um disco suficientemente grande para nunca precisar de o verificar. Um servidor alugado tem um limite fixo de memória, um endereço IP público que começa a ser analisado poucos minutos depois do arranque e um sistema de ficheiros raiz que o Docker vai preencher sem pedir autorização.
Quatro diferenças causam a maioria dos problemas numa máquina pequena:
- A memória é limitada e o kernel resolve a falta de memória terminando um processo.
- Uma porta publicada passa diretamente pela UFW (uncomplicated firewall), porque o Docker escreve as suas próprias regras de firewall.
- Os contentores não voltam a arrancar depois de um reboot, a menos que isso tenha sido configurado antecipadamente.
- As imagens, os contentores, os volumes e a cache de build crescem até o disco ficar cheio.
Cada secção abaixo identifica a falha, a mensagem que verá efetivamente e o guia que a corrige em profundidade. Se ainda não escreveu um ficheiro compose, leia primeiro Noções básicas do Docker Compose numa VPS e depois volte aqui. Esta página pressupõe que já consegue iniciar uma stack.
Quanta RAM um contêiner Docker usa?
Menos do que a maioria das pessoas espera. Um contêiner é um processo num cgroup (grupo de controlo), não uma máquina virtual. Por isso, não existe um kernel convidado nem uma alocação fixa. O custo corresponde ao que o processo interno utiliza. É por isso que uma stack completa cabe em 2 GB quando a mesma stack criada com máquinas virtuais não caberia.
Os valores abaixo são números típicos em estado ocioso para imagens padrão no Ubuntu 24.04 com a configuração predefinida, obtidos a partir de docker stats alguns minutos depois do arranque. Servem como ponto de partida para o planeamento, não como benchmark da sua carga de trabalho. Execute docker stats --no-stream no seu próprio sistema antes de confiar em qualquer valor, incluindo estes.
The data behind this chart
[
{
"label": "nginx",
"idle_mb": 8,
"budget_mb": 64
},
{
"label": "Redis 7",
"idle_mb": 12,
"budget_mb": 128
},
{
"label": "Traefik v3",
"idle_mb": 40,
"budget_mb": 128
},
{
"label": "PostgreSQL 16",
"idle_mb": 45,
"budget_mb": 512
},
{
"label": "Uptime Kuma",
"idle_mb": 95,
"budget_mb": 256
},
{
"label": "MariaDB 11",
"idle_mb": 190,
"budget_mb": 512
},
{
"label": "Nextcloud (Apache)",
"idle_mb": 210,
"budget_mb": 768
}
]As duas colunas têm finalidades diferentes. idle_mb corresponde à memória utilizada pelo contêiner quando não está a fazer nada. budget_mb é o valor a reservar no planeamento, porque a utilização real não ocorre em estado ocioso. O PostgreSQL fica próximo de 45 MB em estado ocioso e precisa de 512 MB quando as ligações, as operações de ordenação e a cache estão ativas. Faça o planeamento com a coluna de orçamento. Faça a depuração com a coluna de estado ocioso.
Observe o padrão dessas 7 linhas. O nginx utiliza 8 MB em estado ocioso e o Nextcloud 210 MB. O proxy à frente das suas aplicações consome pouca memória. É a base de dados e a aplicação PHP que determinam os recursos necessários no sistema.
Tenha atenção a docker stats: o valor de memória inclui a cache de páginas carregadas pelas leituras de ficheiros do próprio contêiner. Por isso, aumenta durante algum tempo depois do arranque e estabiliza a seguir. Monitorize-o durante uma hora antes de concluir que existe uma fuga de memória.
Dimensionamento de um VPS: o que cabe em 2 GB, 4 GB e 8 GB
Primeiro, desconte a parcela usada pelo host. O kernel, o systemd, o journald, o sshd e o daemon do Docker usam a mesma RAM que os seus contentores, e dockerd com containerd ocupa cerca de 100 MB. Também precisa de memória livre para a page cache e para o pico de utilização durante uma compilação de imagem ou a execução de um dump da base de dados.
The data behind this chart
[
{
"plan": "2 GB VPS",
"total_mb": 2048,
"host_reserve_mb": 768,
"container_mb": 1280
},
{
"plan": "4 GB VPS",
"total_mb": 4096,
"host_reserve_mb": 1024,
"container_mb": 3072
},
{
"plan": "8 GB VPS",
"total_mb": 8192,
"host_reserve_mb": 1536,
"container_mb": 6656
}
]host_reserve_mb cobre o sistema operativo, o daemon do Docker e a margem que mantém o servidor responsivo sob carga. O que sobra é container_mb, e esse é o único valor disponível para os contentores. A reserva aumenta com o plano, de 768 MB no servidor mais pequeno para 1536 MB no maior, porque um servidor maior executa mais contentores, escreve mais logs e precisa de mais page cache.
Um plano de 2 GB deixa 1280 MB para os contentores. Se usar 512 MB para o PostgreSQL e 128 MB para o Traefik, metade desse valor já desapareceu. O restante chega para duas aplicações pequenas, com cerca de 256 MB cada. É um servidor real e útil. Não há espaço para o Nextcloud e um cluster de pesquisa adicionais.
Um plano de 4 GB deixa 3072 MB. Nesse espaço cabem ao mesmo tempo uma base de dados, um reverse proxy, três aplicações e um contentor de monitorização. É o tamanho mínimo que vale a pena usar para algo importante, porque a memória disponível absorve um deploy problemático.
Um plano de 8 GB deixa 6656 MB dos seus 8192 MB, e o limite normalmente passa da memória para a CPU ou para o débito do disco. Se os cálculos mostrarem que a sua stack não cabe, compre um plano maior em vez de tentar contornar o problema com ajustes: quanto custa realmente um VPS explica quanto valem os gigabytes adicionais por mês.
Duas regras mantêm os cálculos corretos. Defina um limite de memória para cada serviço, para que um processo descontrolado não derrube todo o servidor. E deixe a parte superior do orçamento por utilizar, porque docker compose build e pg_dump precisam ambos de memória no pior momento. Limites de memória no Docker Compose apresenta a sintaxe e os problemas comuns.
Por que o meu container termina com o código 137?
Porque o kernel o terminou. 137 é 128 mais 9, e o sinal 9 é SIGKILL. O container pediu mais memória do que lhe era permitido, e o OOM killer terminou-o.
CONTAINER ID IMAGE STATUS
9f2c1a4d7b3e postgres:16 Exited (137) 4 minutes agoConfirme a causa em vez de adivinhar:
docker inspect my-db | grep -i oomkilled
sudo journalctl -k | grep -i "out of memory""OOMKilled": true significa que o container atingiu o seu próprio limite de cgroup, e o log do kernel identifica o processo escolhido:
Memory cgroup out of memory: Killed process 2417 (postgres) total-vm:1284416kBEste é o caso bom, porque o impacto ficou limitado a um container. O caso mau é um container sem qualquer limite. Sem um limite, o teto é a memória de toda a máquina. Assim, uma fuga de memória num serviço esgota os recursos do host, e o kernel escolhe depois uma vítima com base no tamanho em todo o sistema. A linha do log perde o prefixo Memory cgroup e passa a ser Out of memory: Killed process 2417 (postgres). O processo escolhido é frequentemente a sua base de dados, enquanto o container que causou a fuga continua a executar. É por isso que definir um limite para todos os serviços é mais importante do que o valor exato de qualquer limite individual.
A swap altera o momento em que o problema ocorre, não a aritmética. A maioria das imagens de VPS é distribuída sem swap. Verifique com swapon --show, que não imprime nada quando não existe swap. Um ficheiro de swap dá ao kernel um local para colocar páginas inativas, o que lhe dá alguns minutos para detetar o problema.
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -hfree -h deve agora mostrar um total diferente de zero na linha Swap. A swap não adiciona RAM. Uma máquina sob pressão constante de memória fica suficientemente lenta para impedir uma ligação SSH, por isso trate a swap como uma margem de segurança e corrija o dimensionamento.
Por que o UFW não bloqueia a porta publicada pelo Docker?
Porque o tráfego nunca chega à cadeia que o UFW protege. Quando publica uma porta com -p 5432:5432 ou com uma entrada ports: do Compose, o daemon escreve uma regra DNAT (tradução de endereços de rede de destino) na tabela nat e uma regra de aceitação na sua própria cadeia DOCKER. Um pacote destinado a um contentor é encaminhado para esse contentor, em vez de ser entregue ao host. Por isso, segue o caminho FORWARD e nunca passa pelas regras INPUT que o UFW escreve.
Pode observar isto no servidor:
sudo ufw status | grep 5432
sudo iptables -t nat -L DOCKER -n | grep 5432O UFW pode mostrar 5432 DENY IN Anywhere enquanto a tabela nat contém uma regra DNAT tcp ... to:172.18.0.2:5432 para a mesma porta. A partir de outra máquina, nc -vz your.server.ip 5432 continua a estabelecer ligação. A base de dados está na Internet pública, mas o firewall indica que não está.
A solução é publicar menos portas. Os contentores do mesmo projeto Compose partilham uma rede e comunicam entre si pelo nome do serviço. Por isso, uma base de dados que serve apenas a aplicação ao lado não precisa de nenhuma entrada ports:. Quando precisar de acesso local, associe a publicação à interface de loopback:
services:
db:
image: postgres:16
ports:
- "127.0.0.1:5432:5432"Depois de docker compose up -d, nc -vz your.server.ip 5432 falha a partir do exterior, enquanto psql -h 127.0.0.1 -p 5432 continua a funcionar no host. Numa stack pequena e saudável, apenas o reverse proxy publica portas: 80 e 443. Por que as portas publicadas pelo Docker ignoram o UFW explica a cadeia DOCKER-USER para os casos em que é necessário publicar uma porta e filtrá-la na mesma. Noções básicas do firewall UFW explica as regras do host subjacentes.
Por que os meus containers desaparecem depois de um reboot?
Porque nada lhes disse para voltar a arrancar. Um container é criado com a política de reinício no se não definir outra, por isso um reboot deixa-o parado e o daemon não toma qualquer ação. Os reboots não são raros numa VPS: atualizações do kernel através de unattended upgrades, manutenção do fornecedor e a sequência de OOM acima acabam todos por provocar um.
Duas condições têm de ser cumpridas. O daemon tem de arrancar no boot:
systemctl is-enabled dockerIsto apresenta enabled numa instalação padrão do Ubuntu. Depois, cada serviço precisa de uma política:
services:
app:
image: ghcr.io/example/app:1.4
restart: unless-stoppedunless-stopped volta a iniciar o container depois de um reboot e respeita um container que tenha parado deliberadamente. always também reinicia os containers que parou intencionalmente sempre que o daemon reinicia, o que pode ser inesperado durante a depuração. Editar o ficheiro não é suficiente, porque a política de reinício é definida quando o container é criado. Execute docker compose up -d para o recriar e, em seguida, verifique o valor em execução:
docker inspect my-app | grep -A3 RestartPolicyDepois, reinicie o sistema deliberadamente e execute docker compose ps no diretório do projeto. Uma stack que sobrevive a um reboot planeado também sobrevive a um reboot não planeado. Se a sua stack precisar de uma garantia de ordem ou de um job executado uma única vez no boot, uma unidade systemd é a ferramenta mais adequada: iniciar o Docker Compose no boot contém o ficheiro da unidade. Para saber se um container que voltou a arrancar está realmente a servir pedidos, adicione healthchecks do Compose.
Por que o disco do meu VPS está cheio?
Porque o Docker mantém tudo até indicar o contrário. Cada tag de imagem que foi obtida, cada container parado, cada volume anónimo deixado para trás por uma recriação e cada camada da cache de build permanece no disco. Num sistema de ficheiros root de 40 GB ou 80 GB, o que é normal nestes tamanhos de plano, isso transforma-se numa indisponibilidade ao fim de meses, e não de anos.
Um disco cheio não se manifesta como uma falha imediata. Recebe no space left on device de um container, de apt, do journald e de docker pull na mesma hora. O PostgreSQL deixa de aceitar escritas. O servidor continua ativo, o que torna o problema mais difícil de detetar do que um ciclo contínuo de reinícios.
Verifique antes de eliminar:
docker system df
df -h /docker system df divide o total entre imagens, containers, volumes locais e cache de build, com uma coluna RECLAIMABLE ao lado de cada item. Num servidor que cria as próprias imagens, a cache de build costuma ser o maior item.
docker image prune -a
docker builder prune
docker system dfdocker image prune -a remove todas as imagens que nenhum container está a utilizar. docker builder prune limpa a cache de build. Ambos são seguros enquanto os serviços estão em execução, porque os recursos em utilização são ignorados. O que não é seguro é docker system prune --volumes, que elimina todos os volumes que nenhum container referencia neste momento. Uma stack parada durante o fim de semana tem exatamente esse formato, e o volume da base de dados é eliminado com ela. Leia bind mounts e volumes nomeados antes de usar essa flag e faça primeiro uma cópia de segurança.
Os logs dos containers são o crescimento mais silencioso. O driver predefinido json-file não tem limite de tamanho, por isso um container com muitos registos pode escrever gigabytes em /var/lib/docker/containers. Defina um limite para todos os containers em /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}Aplique a alteração com sudo systemctl restart docker. Este comando reinicia os seus containers, por isso escolha o momento adequado. O limite aplica-se aos containers criados depois da alteração. Recrie os containers em execução com docker compose up -d --force-recreate e confirme:
docker inspect my-app | grep -A5 LogConfig
sudo du -sh /var/lib/docker/containersA saída do inspect deve mostrar max-size definido. Se estiver vazia, esse container foi criado antes da alteração e continua a escrever sem limite.
Hábitos que mantêm um pequeno servidor Docker saudável
Nada disto exige um dashboard ou uma ferramenta que tenha de aprender.
- Execute
docker system dfedf -h /no primeiro dia do mês. São dois comandos e trinta segundos. Assim, vê a tendência muito antes de ela causar uma indisponibilidade. - Defina um limite de memória para todos os serviços, incluindo os que tem a certeza de serem pequenos. O limite transforma uma indisponibilidade de todo o host num único contentor reiniciado.
- Monitorize o servidor a partir de outro local, para ser avisado sobre pressão de memória ou de disco antes de o kernel atuar. Uptime Kuma é executado num contentor e fica ocioso com cerca de 95 MB.
- Faça cópias de segurança dos volumes, não dos contentores. O contentor é descartável, mas o volume não. cópias de segurança restic numa VPS abrange um agendamento e um teste de restauração.
- Fixe as tags das imagens no ficheiro compose e atualize-as num dia escolhido por si. Com
latest, a versão obtida do próximodocker compose pullé a que foi lançada nessa manhã.
Uma VPS pequena com Docker mantém-se saudável durante anos quando quatro valores permanecem dentro do intervalo esperado: o orçamento de memória, a lista de portas publicadas, a política de reinício de cada serviço e o espaço livre em disco. Todo o resto é o mesmo Docker que já executa em casa.
FAQ
De quanta RAM preciso para executar Docker numa VPS?
O Docker em si consome poucos recursos. O daemon e o containerd ocupam, em conjunto, cerca de 100 MB; o restante requisito depende dos seus contentores. Reserve primeiro a parte do host: 768 MB num servidor com 2048 MB para o sistema operativo, o daemon e alguma margem. Isso deixa 1280 MB disponíveis para os contentores. Uma base de dados com 512 MB, um reverse proxy com 128 MB e duas aplicações pequenas cabem nesse limite. Meça a sua própria stack com docker stats --no-stream em vez de confiar em valores publicados.
Posso executar Docker numa VPS com 1 GB?
Sim, para um ou dois contentores leves. Crie também um ficheiro de swap antes de começar. Cerca de metade de um servidor com 1 GB fica ocupada quando o sistema operativo e o daemon do Docker estão em execução. Sobra espaço para uma aplicação pequena e um reverse proxy, mas não para uma base de dados sob carga real. A criação de imagens num servidor desse tamanho pode falhar ou terminar outro processo. Faça o build noutro local e faça pull da imagem concluída.
O UFW protege um contentor Docker?
Não protege as portas que publicar. O Docker cria as suas próprias regras de DNAT e de encaminhamento. Por isso, um pacote destinado à porta publicada de um contentor é encaminhado para o contentor, em vez de ser entregue ao host. As regras INPUT geridas pelo UFW nunca veem esse pacote. ufw deny 5432 pode estar ativo enquanto essa porta responde a partir da Internet. Publique na loopback com 127.0.0.1:5432:5432, mantenha os serviços internos sem publicação ou filtre na cadeia DOCKER-USER.
Os meus contentores arrancam novamente depois de um reboot da VPS?
Só se tiverem sido criados com uma política de reinício. Defina restart: unless-stopped em cada serviço, execute docker compose up -d para recriar os contentores com essa política e confirme que systemctl is-enabled docker apresenta enabled. Depois, faça um reboot intencionalmente e verifique docker compose ps. Uma política de reinício que nunca foi testada não é uma política de reinício.
Com que frequência devo limpar as imagens Docker?
Uma vez por mês é suficiente para a maioria dos servidores pequenos. Também pode fazer isso sempre que docker system df indicar espaço recuperável que lhe faça falta. docker image prune -a e docker builder prune são seguros enquanto os serviços estão em execução, porque as imagens e a cache em uso são ignoradas. Evite docker system prune --volumes, a menos que saiba exatamente quais volumes não têm referências, porque o comando elimina os dados de qualquer stack que esteja parada.