← Blog

HTTPS pra várias aplicações em um único servidor

Railyard

11 de junho de 2026 · 8 min de leitura

Siga isso no seu próprio servidor. Comece grátis, sem cartão

Uma VPS só já é máquina de sobra pra várias aplicações pequenas: um blog, uma API interna, uma página de status, nenhuma delas precisando de um servidor só pra si. O que elas precisam é que cada uma responda HTTPS no próprio subdomínio, com um certificado que de fato é válido e de fato renova, sem você rodar certbot renew manualmente de tempos em tempos ou escrever uma config de nginx e um cron job separados por site. Uma instância Caddy só na frente de tudo faz isso com menos configuração do que você esperaria, emitindo e renovando um certificado por domínio a partir de um arquivo só.

Este guia configura três aplicações atrás de uma instância Caddy num servidor, cada uma acessível no próprio subdomínio com o HTTPS tratado automaticamente, e depois cobre o que a Railyard automatiza sobre o mesmo problema pra não existir Caddyfile nenhum pra manter.

O que você vai precisar

  • Uma VPS com pelo menos 2 GB de RAM. Um droplet Basic de 2 GB da DigitalOcean (US$12/mês) ou um Hetzner CX23 (2 vCPU, 4 GB de RAM, €5,99/mês sem IVA) tem espaço pra vários containers pequenos mais o próprio Caddy.
  • Um domínio que você controla, com acesso pra adicionar registros de DNS.
  • Docker e o plugin Compose instalados no servidor.
  • As portas 80 e 443 abertas e ainda não usadas por outro servidor web.

Passo 1: aponte o DNS pro servidor antes de qualquer coisa

O Caddy solicita um certificado na primeira vez que cada domínio é requisitado via HTTPS, e o Let’s Encrypt valida essa solicitação checando se o domínio realmente resolve pra esse servidor. Crie um registro A por subdomínio agora, pra já ter tido tempo de propagar quando você iniciar o Caddy:

blog.example.com.    A  203.0.113.10
api.example.com.     A  203.0.113.10
status.example.com.  A  203.0.113.10

Os três podem apontar pro mesmo IP; o Caddy diferencia eles pelo header Host da requisição, do mesmo jeito que qualquer proxy reverso faz.

Passo 2: organize as aplicações como containers separados

Este exemplo roda três coisas: uma aplicação de blog e uma API, cada uma seu próprio container pequeno, e uma página de status servida como arquivos estáticos simples sem backend nenhum. Cada aplicação mora no próprio diretório com o próprio Dockerfile:

.
├── blog/
│   └── Dockerfile
├── api/
│   └── Dockerfile
├── status/
│   └── index.html
├── docker-compose.yml
└── Caddyfile

Nada nisso depende do que realmente está dentro de blog ou api; uma aplicação Django de um guia anterior, uma API em Node, ou qualquer outra coisa que escute numa porta dentro do próprio container funciona do mesmo jeito. O único requisito que importa pro Caddy é que cada container exponha uma porta na rede Docker.

Passo 3: escreva o Caddyfile

{
	email ops@example.com
}

blog.example.com {
	reverse_proxy blog:8000
}

api.example.com {
	reverse_proxy api:8001
}

status.example.com {
	root * /srv/status
	file_server
}

Cada domínio é o próprio bloco. blog.example.com e api.example.com fazem proxy pros containers deles pelo nome do serviço no Docker Compose; o Caddy resolve esse nome na rede interna do mesmo jeito que resolveria um hostname em qualquer outro lugar. status.example.com não tem backend nenhum, o Caddy só serve os arquivos direto do disco com file_server. O bloco email no topo é o que o Let’s Encrypt usa pra te avisar se um certificado está perto de falhar a renovação; é opcional mas vale configurar.

Valide um Caddyfile antes de confiar nele em produção:

docker run --rm -v "$(pwd)/Caddyfile:/etc/caddy/Caddyfile" caddy:2 caddy validate --config /etc/caddy/Caddyfile

Você deve ver Valid configuration impresso, não um erro de parse.

Passo 4: escreva o docker-compose.yml

services:
  blog:
    build: ./blog
    restart: unless-stopped
    expose:
      - "8000"

  api:
    build: ./api
    restart: unless-stopped
    expose:
      - "8001"

  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - ./status:/srv/status:ro
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      - blog
      - api

volumes:
  caddy_data:
  caddy_config:

Só o caddy publica as portas 80 e 443 pro host; blog e api usam expose, que deixa as portas deles acessíveis a partir de outros containers na mesma rede do Compose, mas não de fora do servidor de forma alguma. É esse o ponto de colocar tudo atrás de um proxy só: os containers de aplicação nunca são acessíveis diretamente, só através do Caddy, e só no domínio que deveriam responder. O volume caddy_data é o que faz os certificados sobreviverem a um restart; perdê-lo significa que o Caddy solicita certificados novos ao Let’s Encrypt na próxima vez que iniciar, o que é seguro mas conta contra os limites de taxa do Let’s Encrypt se acontecer com frequência.

Passo 5: inicie e cheque os certificados

docker compose up -d --build
docker compose logs -f caddy

Você deve ver uma linha pra cada domínio dizendo que um certificado foi obtido, algo como certificate obtained successfully com o nome do domínio do lado. Confirme de fora do servidor:

curl -I https://blog.example.com
curl -I https://api.example.com
curl -I https://status.example.com

Cada um deve retornar uma resposta sem aviso de certificado, e curl -v mostraria um certificado emitido pelo Let’s Encrypt pra exatamente esse hostname.

Passo 6: adicione uma quarta aplicação depois sem downtime

Adicione um novo bloco no Caddyfile, um novo serviço no docker-compose.yml, crie o registro de DNS dele, e então diga ao container Caddy que já está rodando pra pegar a mudança sem reiniciar mais nada:

docker compose up -d newapp
docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile

caddy reload relê a configuração e só toca no que mudou; os certificados e as conexões dos outros três domínios ficam intocados.

Backups e upgrades

O único estado que vale a pena fazer backup aqui é o volume caddy_data (certificados, que regeneram de qualquer jeito) e o que quer que cada aplicação em si armazene, como um volume de banco de dados se alguma delas tiver um. Faça backup disso do mesmo jeito que faria pra qualquer aplicação: um dump agendado ou um snapshot de volume copiado pra fora do servidor.

Pra atualizar o próprio Caddy, baixe a nova tag de imagem e recrie só esse container:

docker compose pull caddy
docker compose up -d caddy

Os certificados já existentes no caddy_data são preservados; o Caddy não precisa reemitir nada só porque a versão do binário mudou.

O caminho mais rápido: faça isso com a Railyard

Tudo o que foi feito acima (o certificado por domínio, o proxy reverso, a checagem de propriedade que impede outra pessoa de apontar um domínio que não controla pro seu servidor) é o que a Railyard automatiza por aplicação em vez de um Caddyfile compartilhado só. Toda aplicação recebe https://<name>.<server-ip>.sslip.io com um certificado desde o momento do deploy, sem precisar de registro de DNS ainda. Adicionar o seu próprio domínio significa abrir a aba Domains da aplicação, adicionar o hostname, e criar o registro A ou CNAME que ela te mostra; um registro TXT (_railyard-verify.<domain>) comprova que você controla o domínio antes do certificado ou do roteamento começar, checado automaticamente a cada 30 segundos. Rodar várias aplicações no mesmo servidor não muda nada disso: cada uma é roteada e certificada independentemente, do mesmo jeito que o Caddyfile acima trata três domínios numa configuração só, só que sem você escrever ou recarregar essa configuração sozinho.

Pra um domínio wildcard (*.example.com roteando todo subdomínio pra uma aplicação) ou muitos subdomínios que de outra forma significariam um certificado por visita, Networking cobre as duas opções: TLS sob demanda (um certificado emitido na primeira vez que cada subdomínio é visitado, o padrão) ou um certificado só pro wildcard inteiro através de um desafio DNS-01, usando um token de API do Cloudflare ou uma chave de acesso do Route 53 adicionada em Team settings → DNS credentials.

Resolução de problemas

Porta 80 ou 443 já em uso. Alguma outra coisa (geralmente um nginx ou Apache instalado pela imagem do servidor, ou um resquício de uma configuração anterior) já está ligada a ela. Encontre com ss -tlnp | grep -E ':80|:443' e pare antes de iniciar o Caddy.

Um certificado nunca é emitido pra um domínio mas os outros funcionam normalmente. Cheque o DNS desse domínio específico separadamente: dig blog.example.com. Um erro de digitação num registro A é a causa mais comum, e só quebra esse domínio porque o Caddy solicita certificados independentemente por bloco de site.

“too many certificates already issued” do Let’s Encrypt. Isso é um limite de taxa (atualmente 5 certificados duplicados por domínio exato por semana), geralmente atingido por derrubar e recriar repetidamente o volume caddy_data enquanto testa. Mantenha o volume entre restarts, e use a CA interna do Caddy (tls internal no bloco do Caddyfile) enquanto itera na configuração, mudando pra certificados reais quando estiver funcionando.

Erros de permissão de arquivo na página de status ou num volume montado. O file_server precisa de acesso de leitura ao que quer que esteja montado na raiz dele; um volume criado por um container diferente rodando como um usuário diferente é a causa mais comum. Corrija a propriedade no host, ou monte como :ro como no exemplo acima se nada precisar escrever ali.

O servidor fica sem memória com vários containers mais o Caddy. Cada container de aplicação pequena geralmente está bem, mas cheque o uso real com docker stats antes de assumir que é o Caddy; um container de banco de dados é um suspeito mais provável do que o próprio proxy. Aumente o tamanho, ou mova a aplicação mais pesada pro próprio servidor e continue apontando o subdomínio dela pra onde quer que ela realmente rode.

Perguntas frequentes

Eu preciso de Docker pra isso, ou funciona com um Caddy instalado no sistema? O padrão é idêntico nos dois casos: um Caddyfile com um bloco por domínio, cada um apontando pra uma porta local em vez do nome de um serviço Docker. O systemd gerencia o processo do Caddy em vez do Compose.

Todas as aplicações precisam estar no mesmo servidor? Pra este guia, sim, já que o ponto é uma instância Caddy compartilhada. Nada impede você de rodar o Caddy num servidor e fazer proxy pra aplicações em outros através de uma rede privada, mas nesse ponto você está construindo o tipo de roteamento que uma plataforma como a Railyard já trata.

O que acontece se eu adicionar cem subdomínios? O TLS sob demanda emite um certificado por subdomínio na primeira vez que é visitado, o que funciona mas significa cem certificados do Let’s Encrypt separados pra acompanhar. Um certificado wildcard através de DNS-01 cobre todos eles com um só, que é a escolha melhor a partir de uns poucos subdomínios, tanto num Caddyfile escrito à mão quanto através da própria opção de credencial de DNS da Railyard.

Isso redireciona HTTP pra HTTPS automaticamente? Sim, o Caddy faz isso por padrão pra todo domínio no Caddyfile sem precisar de configuração extra.

Três domínios atrás de uma instância Caddy e trinta atrás de uma não são configurações significativamente diferentes, o Caddyfile só fica mais longo. A parte que vale internalizar é que um proxy reverso emitindo os próprios certificados por domínio é mais simples de rodar do que um cron job de certbot e uma config de nginx por site, seja você escrevendo esse Caddyfile sozinho ou deixando outra coisa escrever pra você.

Por que escolhem o Railyard para isso

  • Sua própria conta de cloud. Faça deploy no DigitalOcean, Hetzner, Vultr, AWS, OVHcloud, ou em qualquer servidor Linux via SSH, cobrado direto pelo seu provedor, sem markup.
  • Um preço fixo por equipe. O Free é grátis para sempre: um servidor mais um servidor de banco de dados, três apps, sem cartão. O Pro custa US$19 por mês para até cinco servidores e apps ilimitados. O Team custa US$49 por mês com servidores ilimitados. Sem cobrança por app, por deploy, por assento ou por gigabyte.
  • Templates de um clique. Faça deploy de apps open source, como bancos de dados, analytics e ferramentas de dev, no seu próprio servidor em um clique.
  • Bancos de dados gerenciados. Postgres, MySQL e Redis com backups programados, verificados por restauração, e recuperação point-in-time.
  • Push-to-deploy. Faça deploy a partir do GitHub, GitLab ou da CLI, com HTTPS automático e releases sem downtime.

Pule o Caddyfile compartilhado

Toda aplicação que você faz deploy na Railyard recebe o próprio endereço HTTPS automaticamente, sem Caddyfile pra manter manualmente.

Plano grátis, sem cartão, e 14 dias de Pro para cada equipe nova. Um preço fixo por equipe.