Docker numa VPS: o que muda na prática
O Docker numa VPS comporta-se de forma diferente do desktop. Saiba como evitar que o disco encha, como a UFW ignora portas publicadas e como garantir o restart dos containers.
O que muda quando 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 livre, uma firewall que ninguém está a analisar e um disco suficientemente grande para nunca precisar de o consultar. 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 é finita e o kernel resolve uma 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 tenha configurado isso 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 volte depois. Esta página pressupõe que já consegue iniciar uma stack.
Quanta RAM um contentor Docker utiliza?
Menos do que a maioria das pessoas espera. Um contentor é 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 no seu interior 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 idle para imagens padrão no Ubuntu 24.04, com a configuração predefinida, lidos 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 servidor antes de confiar em qualquer valor, incluindo estes.
Os dados por trás deste gráfico
[
{
"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 funções diferentes. idle_mb indica o que o contentor utiliza quando não está a fazer nada. budget_mb indica quanto deve reservar no planeamento, porque a utilização real não ocorre em idle. O PostgreSQL fica perto de 45 MB em idle e precisa de 512 MB quando as ligações, ordenações 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 idle.
Observe o perfil dessas 7 linhas. O nginx fica em idle com 8 MB e o Nextcloud com 210 MB. O proxy à frente das suas aplicações consome muito pouca memória. São a base de dados e a aplicação PHP que determinam a dimensão necessária do servidor.
Tenha atenção a docker stats: o valor de memória inclui a cache de páginas carregada pelas leituras de ficheiros do próprio contentor, por isso aumenta durante algum tempo depois do arranque e depois estabiliza. 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
Comece por descontar a parcela do host. O kernel, o systemd, o journald, o sshd e o daemon do Docker usam a mesma RAM que os seus containers, e dockerd com containerd ocupa cerca de 100 MB. Também precisa de memória livre para a cache de páginas e para o pico que ocorre quando é criada uma imagem ou executado um dump de uma base de dados.
Os dados por trás deste gráfico
[
{
"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 que pode gastar. A reserva aumenta com o plano, de 768 MB no servidor mais pequeno para 1536 MB no maior, porque um servidor maior executa mais containers, escreve mais logs e precisa de mais cache de páginas.
Um plano de 2 GB deixa 1280 MB para os containers. Gaste 512 MB desse valor com PostgreSQL e 128 MB com Traefik, e metade já desapareceu. O restante permite executar duas aplicações pequenas, com cerca de 256 MB cada. É um servidor real e útil. Não é suficiente para adicionar o Nextcloud e um cluster de pesquisa.
Um plano de 4 GB deixa 3072 MB. Esse valor permite executar ao mesmo tempo uma base de dados, um reverse proxy, três aplicações e um container de monitorização. É o menor tamanho que vale a pena usar para algo importante, porque a memória disponível absorve os efeitos de uma implementação defeituosa.
Um plano de 8 GB deixa 6656 MB dos seus 8192 MB, e o limite normalmente passa da memória para o CPU ou para a taxa de transferência do disco. A SSD Nodes não vende nada mais pequeno: o seu plano mais pequeno tem 8 GB de RAM (página de preços consultada em 2026-10-11). Nesse host, esta é a linha aplicável, e as linhas de 2 GB e 4 GB descrevem servidores mais pequenos de outros fornecedores. Uma classe de containers dimensiona-se a partir da configuração, e não da carga: um servidor de modelos local reserva a cache KV proporcionalmente ao seu contexto, por isso aumentar o num_ctx do Ollama pode adicionar gigabytes ao orçamento antes de chegar um único pedido. Se os cálculos mostrarem que a sua stack não cabe, compre o 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 sem utilizar, porque docker compose build e pg_dump precisam de memória no pior momento. Limites de memória no Docker Compose apresenta a sintaxe e as armadilhas.
Porque é 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 aquela que lhe era permitida, 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 favorável, porque o impacto ficou limitado a um container. O caso problemático é um container sem qualquer limite. Sem um limite, o teto é a memória de toda a máquina. Uma fuga de memória num serviço esgota os recursos do host, e o kernel escolhe depois uma vítima com base no consumo em todo o sistema. A linha do log perde o prefixo Memory cgroup e fica 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, definir um limite para todos os serviços é mais importante do que acertar no valor exato de qualquer limite individual.
A swap altera o momento em que o problema surge, não a aritmética. A maioria das imagens VPS é fornecida 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 compra 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 o acesso por SSH e a correção do problema. 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 protegida pelo UFW. 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 escritas pelo UFW.
Pode observar este comportamento 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á acessível através da Internet pública, embora a firewall indique o contrário.
A correção é publicar menos portas. Os contentores do mesmo projeto compose partilham uma rede e conseguem aceder uns aos outros pelo nome do serviço. Por isso, uma base de dados que serve apenas a aplicação ao lado não precisa de qualquer 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 corretamente configurada, apenas o reverse proxy publica portas, nas portas 80 e 443. Por que as portas publicadas pelo Docker ignoram o UFW explica a cadeia DOCKER-USER nos casos em que é necessário publicar uma porta e filtrá-la na mesma. Noções básicas da firewall UFW explica as regras do host subjacentes.
Por que os meus containers desaparecem depois de um reboot?
Porque nada lhes disse para voltarem 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 intervém. 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 OOM acima terminam todos num deles.
Duas condições têm de ser cumpridas. O daemon tem de arrancar no boot:
systemctl is-enabled dockerIsto apresenta enabled numa instalação Ubuntu padrão. Depois, cada serviço precisa de uma política:
services:
app:
image: ghcr.io/example/app:1.4
restart: unless-stoppedunless-stopped volta a arrancar o container depois de um reboot e respeita um container que parou de propósito. always também reinicia os containers que parou deliberadamente 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, depois, confirme o valor ativo:
docker inspect my-app | grep -A3 RestartPolicyDepois, faça reboot do servidor de propósito 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 é uma ferramenta melhor: iniciar Docker Compose no boot inclui o ficheiro da unidade. Para confirmar se um container que voltou a arrancar está efetivamente a servir pedidos, adicione healthchecks do Compose.
Por que o disco da minha VPS está cheio?
Porque o Docker mantém tudo até que você diga o contrário. Cada tag de imagem que você baixou, cada contentor parado, cada volume anónimo deixado para trás por uma recriação e cada camada da cache de compilação permanecem no disco. Um disco maior apenas adia o problema. Até o SSD de 160 GB do plano mais pequeno da SSD Nodes (página de preços consultada em 2026-10-11) fica cheio da mesma forma num sistema que descarrega e compila imagens todas as semanas, e um sistema de ficheiros raiz mais pequeno transforma isso numa indisponibilidade mais cedo.
Um disco cheio não se manifesta necessariamente como uma falha. Na mesma hora, você recebe no space left on device de um contentor, de apt, do journald e de docker pull. O PostgreSQL deixa de aceitar escritas. O sistema continua ativo, o que torna o problema mais difícil de detetar do que um ciclo de reinícios.
Verifique antes de apagar:
docker system df
df -h /docker system df divide o total entre imagens, contentores, volumes locais e cache de compilação, com uma coluna RECLAIMABLE ao lado de cada item. Num sistema que compila as próprias imagens, a cache de compilação costuma ser o maior item.
docker image prune -a
docker builder prune
docker system dfdocker image prune -a remove todas as imagens que nenhum contentor está a utilizar. docker builder prune limpa a cache de compilação. Ambos são seguros enquanto os serviços estão em execução, porque os itens em uso são ignorados. O que não é seguro é docker system prune --volumes. Este comando remove os contentores parados e, depois, todos os volumes anónimos que nenhum contentor restante utiliza. Uma stack que você parou durante o fim de semana tem exatamente essa configuração. Se a base de dados tiver mantido os dados num volume anónimo, que é o volume criado pelo Docker quando uma imagem declara um volume e o seu ficheiro compose não monta nada nesse caminho, esses dados são removidos com o volume. Os volumes nomeados sobrevivem a esse comando, mas docker volume prune --all também remove os volumes nomeados não utilizados. Leia bind mounts versus volumes nomeados antes de utilizar qualquer uma das flags e faça primeiro uma cópia de segurança.
Os logs dos contentores são uma forma mais silenciosa de crescimento. O driver json-file predefinido não tem limite de tamanho, por isso um contentor com muitos registos escreve gigabytes em /var/lib/docker/containers. Defina um limite para todos os contentores 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 contentores, por isso escolha o momento adequado. O limite aplica-se aos contentores criados depois da alteração. Recrie os contentores 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 de inspect deve mostrar max-size definido. Se estiver vazia, o contentor 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 de cada mês. São dois comandos, demoram trinta segundos e permitem identificar a tendência muito antes de esta 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 da pressão sobre a memória ou o disco antes de o kernel atuar. O Uptime Kuma é executado num contentor e fica inativo 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 num VPS abrange a definição de um agendamento e um teste de restauro.
- Fixe as tags das imagens no ficheiro compose e atualize-as num dia escolhido por si. Com
latest, a versão obtida no próximodocker compose pullé a que foi lançada nessa manhã.
Um VPS pequeno com Docker mantém-se saudável durante anos quando quatro valores permanecem dentro dos limites: 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. Tudo o resto é o mesmo Docker que já executa em casa.
FAQ
Quanta RAM é necessária para executar o Docker numa VPS?
O Docker, por si só, consome poucos recursos. O daemon e o containerd ocupam em conjunto cerca de 100 MB; o restante depende dos seus contentores. Reserve primeiro a parte do host: 768 MB numa máquina com 2048 MB para o sistema operativo, o daemon e alguma margem. Ficam 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 o Docker numa VPS com 1 GB?
Sim, para um ou dois contentores leves. Crie um ficheiro de swap antes de começar. Cerca de metade dos 1 GB fica ocupada quando o sistema operativo e o daemon do Docker estão em execução. Isso deixa espaço para uma aplicação pequena e um reverse proxy, mas não para uma base de dados sob carga real. Criar imagens numa máquina desse tamanho pode falhar ou terminar outro processo. Faça a compilação noutro local e descarregue a imagem pronta.
O UFW protege um contentor Docker?
Não protege as portas que publica. O Docker escreve as suas próprias regras DNAT e de encaminhamento. Assim, um pacote destinado à porta publicada de um contentor é encaminhado para o contentor, em vez de ser entregue ao host, e nunca passa pelas regras INPUT geridas pelo UFW. ufw deny 5432 pode estar ativo enquanto essa porta responde à Internet. Publique a porta no 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 reiniciam 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 remover imagens do Docker?
Uma vez por mês é suficiente para a maioria dos servidores pequenos. Também pode fazer essa limpeza quando 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 ignoram as imagens e a cache em utilização. Evite docker system prune --volumes, a menos que saiba exatamente que volumes não têm referências. Esse comando elimina os volumes anónimos de qualquer stack que esteja parada, e docker volume prune --all também inclui os volumes nomeados não utilizados.