Como publicar uma aplicação Astro com SSR e Docker em uma VPS

Fala aí, beleza? Se o seu projeto Astro precisa de login, formulário, dado atualizado a cada requisição ou alguma lógica no servidor, só subir a pasta de build não resolve 🙂
Tenha acesso root e SSD NVMe, com 10% OFF extra pelo link da Hora de Codar.
Ver a oferta de VPS Parceria Hora de CodarAqui o caminho é este: adapter Node em modo standalone, imagem Docker multi-stage baseada na receita oficial e um proxy reverso cuidando do HTTPS na VPS
Você vai precisar de um projeto Astro atualizado, Docker, uma VPS com acesso root e um domínio já apontado para o IP do servidor
Site estático ou aplicação SSR: quando você precisa do adapter Node
Você só precisa do adapter Node quando alguma página ou endpoint for renderizado no servidor, no momento da requisição
Por padrão, o Astro pré-renderiza o site inteiro e manda HTML estático pro navegador. É o output: 'static', a saída padrão (guia de renderização sob demanda)
Na prática, existem três cenários:
- Site 100% estático: fica no
output: 'static'e não roda Node em produção. A receita oficial de Docker do Astro serve o build com Apache httpd na porta 80 ou com NGINX, que exige criar o arquivonginx/nginx.confna raiz do projeto - Site majoritariamente estático com algumas rotas dinâmicas: continua em
output: 'static'e você colocaexport const prerender = falseno topo da página ou do endpoint que precisa ser dinâmico. O resto segue estático, mas essas rotas já exigem um adapter - Aplicação inteira sob demanda:
output: 'server'com o adapter Node, e todo o projeto passa a ser renderizado no servidor
Se liga: renderização sob demanda sem adapter não existe
Em VPS com Node, o adapter é o @astrojs/node. Além das rotas sob demanda, ele suporta server islands, actions e sessions (guia do adapter Node)
Standalone ou middleware?
O @astrojs/node tem dois modos:
- standalone: gera um servidor que inicia sozinho quando o módulo de entrada é executado
- middleware: pra encaixar o Astro dentro de outro servidor Node, como Express ou Fastify
Este tutorial usa standalone porque é exatamente o que o Dockerfile oficial espera: um processo Node subindo sozinho dentro do contêiner, sem servidor extra no meio
Se você conhece um app Node "normal" que roda com node server.js, é bem semelhante 😀
O que você precisa antes de começar
Antes de abrir o terminal, confira esta lista:
- Projeto Astro na linha atual: a versão estável é da linha 7, com a 7.4.0 publicada em 15/10/2026 (releases do Astro)
- Node compatível: desde o Astro 6, Node 18 e Node 20 não são mais suportados, e o mínimo passou a ser Node 22.12.0, tanto no desenvolvimento quanto no deploy. Na versão atual da linha 7 ([email protected], publicada em 15/10/2026), esse mínimo subiu para Node 22.22.0 (releases do Astro): confirme a exigência da sua versão instalada antes de fixar a imagem
- Docker instalado na máquina onde você vai gerar a imagem
- VPS com acesso root, pra instalar e controlar os contêineres
- Domínio apontado para o IP da VPS, que você vai usar no passo do proxy reverso com HTTPS
Qual VPS usar para este projeto?
A recomendação da Hora de Codar para este cenário é a VPS da Hostinger (este artigo contém links de afiliado da Hostinger)
O motivo é a tarefa: o tutorial depende de Docker, proxy reverso e acesso total ao servidor, e a Hostinger reúne essas peças prontas: template Docker, Docker Manager e um guia próprio para o Nginx Proxy Manager
- Acesso root, com controle total do servidor
- Template Ubuntu 24.04 que já vem com
docker-ceedocker-composeinstalados - Docker Manager, ferramenta web incluída sem custo nas VPS com o template Docker, pra implantar e monitorar contêineres sem linha de comando
- Nginx Proxy Manager, implantado pelo Catálogo do próprio Docker Manager, com emissão de certificado Let’s Encrypt gratuito pela aba SSL
- Processadores AMD EPYC e SSD NVMe
- Rede de 1 Gbps, com servidores em várias partes do mundo
- Backups semanais gratuitos
Como referência de especificação, o plano KVM 2 tem 2 vCPU, 8 GB de RAM e 100 GB de disco NVMe. Isso é só referência, não um mínimo obrigatório: o tamanho certo depende do tráfego e do que a sua aplicação faz
Passo a passo: do adapter Node ao HTTPS na VPS
O resultado final: sua aplicação Astro rodando em um contêiner Node na VPS, acessível pelo seu domínio em HTTPS
Bora ver na prática?
Passo 1: instalar o adapter Node
- Na raiz do projeto, rode:
npx astro add node
Esse comando instala o @astrojs/node e já ajusta o astro.config numa etapa só
Resultado esperado: o pacote aparece nas dependências e o adapter fica registrado na configuração
O erro comum deste passo: pular o adapter e esperar que prerender = false ou output: 'server' funcionem sozinhos. Sem adapter não tem renderização sob demanda
Passo 2: configurar o astro.config.mjs
- Defina o modo de saída, o adapter em standalone e os domínios permitidos. Exemplo didático, troque o domínio pelo seu:
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';
export default defineConfig({
output: 'server',
adapter: node({ mode: 'standalone' }),
security: {
allowedDomains: [
{ hostname: 'exemplo.com.br', protocol: 'https' }
]
}
});
Se o site é quase todo estático, mantenha o output padrão e marque só as rotas dinâmicas no frontmatter da página:
---
export const prerender = false;
---
E por que o allowedDomains?
Mais controle no próximo deploy.
Configure sua aplicação em uma VPS Hostinger com acesso root. Escolha os recursos conforme o que seu projeto precisa.
- SSD NVMe
- AMD EPYC
- Backups semanais
A aplicação vai ficar atrás de um proxy reverso. A opção security.allowedDomains define os padrões de host permitidos nas requisições SSR: protocol, hostname e port: e, com ela configurada, o Astro valida o cabeçalho X-Forwarded-Host contra esses padrões (referência de configuração)
Tome cuidado com versão antiga: antes da 5.14.2, o Astro refletia o valor de X-Forwarded-Host em Astro.url sem validação, falha registrada como CVE-2025-61925. Mais um motivo pra estar na linha atual
Resultado esperado: o build passa a gerar a parte de servidor em dist/server/
O erro comum deste passo: não configurar allowedDomains com o domínio real da sua aplicação. Como o Astro valida X-Forwarded-Host contra os padrões dessa lista, um domínio ausente ou errado pode fazer Astro.url não refletir o host que você esperava: depois do deploy, sempre confira esse valor numa rota real
Passo 3: criar o .dockerignore
- Crie um arquivo
.dockerignorena raiz do projeto
Ele funciona como o .gitignore: define arquivos que o Docker ignora nos comandos COPY e ADD. A receita oficial recomenda esse arquivo porque acelera o build e reduz o tamanho da imagem
Liste ali o que não precisa ir pra dentro da imagem, como dependências e builds gerados na sua máquina
Resultado esperado: a etapa de envio de contexto do build fica mais leve
Passo 4: escrever o Dockerfile multi-stage
- Use como base o Dockerfile de SSR da receita oficial, que exige o
@astrojs/nodee serve o site com Node.js na porta 4321
Que multi-stage? É dividir o build em estágios, cada um com uma função, e copiar pra imagem final só o que interessa. A estrutura oficial é esta:
- base:
FROM node:lts AS base, copiapackage.jsonepackage-lock.json - prod-deps: instala apenas as dependências de produção
- build-deps: instala as dependências de build
- build: copia o projeto e executa o build
- runtime: copia o
node_modulesde produção e odist
O esqueleto abaixo mostra essa estrutura. Complete as linhas de instalação e cópia de cada estágio com os comandos da receita oficial:
FROM node:lts AS base
# copia package.json e package-lock.json
FROM base AS prod-deps
# instala só as dependências de produção (comando na receita oficial)
FROM base AS build-deps
# instala as dependências de build (comando na receita oficial)
FROM build-deps AS build
# copia o projeto e roda o build
FROM base AS runtime
# copia node_modules de prod-deps e dist de build
ENV HOST=0.0.0.0
ENV PORT=4321
EXPOSE 4321
CMD ["node", "./dist/server/entry.mjs"]
O grande ganho aqui é o cache: quando só o código-fonte muda, o Docker não reinstala as dependências npm, o que pode economizar minutos em cada build
Se você já empacotou outro runtime, a lógica é parecida com a de hospedar Bun com Docker
Resultado esperado: uma imagem que, ao iniciar, roda node ./dist/server/entry.mjs escutando na porta 4321
O erro comum deste passo: esquecer o ENV HOST=0.0.0.0 no Dockerfile. A receita oficial inclui essa variável ao lado de PORT=4321, então mantenha as duas exatamente como no Dockerfile oficial pra não ter surpresa na hora de ligar o proxy
Outro ponto: a imagem node:lts precisa atender ao Node mínimo exigido pela sua versão do Astro. Desde o Astro 6 esse mínimo é 22.12.0, mas na linha 7 atual ([email protected]) ele subiu para 22.22.0: confira qual versão a tag lts está apontando no momento do build e fixe a versão da imagem se precisar garantir o mínimo exigido
Passo 5: variáveis de ambiente e segredos
- Organize as variáveis antes de subir o contêiner
No Astro, as variáveis são acessadas com import.meta.env. Em SSR, elas podem ser lidas em tempo de execução conforme o adapter, e na maioria dos adapters isso é feito via process.env (guia de variáveis de ambiente)
Pra segredos, a API astro:env permite definir um schema tipado. Segredo de servidor usa context: 'server' com access: 'secret', e o helper getSecret() lê o valor bruto de uma variável
No modo standalone, HOST e PORT também podem ser sobrescritos em runtime pelas variáveis de ambiente, sem rebuildar a imagem
Vai receber upload? O @astrojs/node tem a opção bodySizeLimit (adicionada na versão 10.0.0 do adapter), que define o tamanho máximo do corpo da requisição no servidor standalone. O padrão é 1 GB, então ajuste pro que faz sentido no seu caso
O erro comum deste passo: esperar que um segredo marcado como servidor apareça no código do cliente. Ele foi feito justamente pra não aparecer

Crie sistemas com IA para vender
Prompts, modelos e checklists para criar e publicar seu sistema com IA e preparar a oferta para o primeiro cliente.
Quero o e-book gratuito Receba o livro e os bônus no seu e-mail.Passo 6: preparar a VPS com o template Docker
- No hPanel da Hostinger, acesse VPS → Manage → OS & Panel → Operating System e escolha o template com Docker entre os sistemas operacionais disponíveis
Esse template entrega Ubuntu 24.04 com docker-ce e docker-compose já instalados (documentação do template Docker)
Tome cuidado! Trocar ou reinstalar o sistema operacional apaga permanentemente todos os dados da VPS, inclusive o snapshot (documentação sobre troca de SO). Faça isso antes de ter qualquer dado importante na máquina, senão é um rm -rf da vida sem você ter digitado nada haha
Resultado esperado: VPS com Docker pronto e o Docker Manager disponível no painel
Ainda não tem o servidor? Dá pra comparar os planos de VPS da Hostinger e escolher conforme o tamanho do projeto
Passo 7: subir o contêiner com o Docker Manager
- Abra o Docker Manager no painel da VPS
Ele é a interface web pra implantar, iniciar, parar e monitorar contêineres, e consegue subir imagens publicadas no Docker Hub sem linha de comando (artigo da Hostinger sobre o Docker Manager)
Se você publicar a imagem do passo 4 num registry como o Docker Hub, use esse caminho direto no Docker Manager. Ele também tem as opções "Compose manually" e "Compose from URL", úteis quando você prefere não publicar a imagem antes de implantar. Configure as variáveis de ambiente do passo 5 em qualquer um desses caminhos
Resultado esperado: contêiner do Astro em execução, aparecendo como ativo no Docker Manager
Passo 8: proxy reverso com HTTPS
- Instale o Nginx Proxy Manager pelo próprio Docker Manager, seguindo o guia da Hostinger
E por que um proxy na frente? Ele recebe o tráfego do domínio em HTTPS e repassa pro contêiner do Astro, que segue falando HTTP só dentro do servidor
A configuração fica assim:
- Coloque o contêiner do Astro na mesma rede Docker do Nginx Proxy Manager
- Crie o proxy host apontando para o nome do contêiner do Astro na porta 4321
- Na aba SSL, emita o certificado Let’s Encrypt gratuito
Encaminhar pelo nome do contêiner, em vez de IP e porta expostos, é a prática recomendada pela Hostinger quando vários serviços ficam atrás do mesmo proxy
Se depois a sua stack crescer com API e banco, o raciocínio é o mesmo de uma API NestJS com Compose
O erro comum deste passo: apontar o proxy direto para IP e porta do contêiner em vez da rede Docker. O guia da Hostinger usa IP:porta como fluxo básico, mas recomenda a rede Docker com o nome do contêiner quando há vários serviços atrás do mesmo proxy
Checagem final
Confira estes três pontos:
- O domínio abre em
https://com certificado válido - Uma rota com
prerender = false(ou qualquer rota, se você usaoutput: 'server') responde de forma dinâmica, com conteúdo gerado na requisição Astro.urlreflete o domínio que você configurou emallowedDomains
Passou nos três? Aplicação no ar, mto massa! 😀
Erros comuns ao publicar Astro SSR com Docker e como resolver
A maioria dos problemas aqui é de configuração, não de servidor. Antes de pensar em trocar de plano, confira esta lista
O build ou o start falha por versão do Node
- Sintoma: erro logo no build ou ao iniciar o contêiner
- Causa: Node antigo. Desde o Astro 6, Node 18 e 20 não são suportados; na versão atual do Astro (linha 7, [email protected]), o mínimo subiu para Node 22.22.0
- Solução: use uma imagem que atenda ao mínimo exigido pela sua versão exata do Astro
- Como prevenir: fixe a versão da imagem no Dockerfile em vez de depender de uma tag que muda sozinha
O proxy não alcança a aplicação
- Sintoma: erro de gateway ou timeout ao abrir o domínio
- Causa: servidor sem
HOST=0.0.0.0, porta diferente de 4321 ou contêiner fora da rede do proxy - Solução: confira
HOSTePORTno contêiner e coloque o Astro na mesma rede Docker do Nginx Proxy Manager - Como prevenir: aponte o proxy sempre para o nome do contêiner na porta certa
Host ou URLs errados atrás do proxy
- Sintoma:
Astro.urlnão mostra o seu domínio - Causa:
security.allowedDomainssem o domínio correto configurado - Solução: configure
hostnameeprotocoldo seu domínio - Como prevenir: inclua essa opção já no primeiro deploy, como no passo 2, e confira o valor de
Astro.urlnuma rota real depois do deploy
Upload ou corpo de requisição rejeitado
- Sintoma: requisições grandes falham no servidor
- Causa: limite definido em
bodySizeLimitno@astrojs/node - Solução: ajuste o valor conforme o tamanho que sua aplicação realmente precisa receber
A página que deveria ser dinâmica sai estática
- Sintoma: o conteúdo não muda entre requisições
- Causa: a rota foi pré-renderizada, porque falta
export const prerender = falseououtput: 'server' - Solução: marque a rota ou mude o modo de saída do projeto
Integrações incompatíveis depois de atualizar
- Sintoma: erro de build após subir a versão do Astro
- Causa: Astro e adapter em versões que não conversam entre si
- Solução: rode
npx @astrojs/upgrade, que atualiza o Astro e as integrações oficiais, incluindo o adapter, para versões compatíveis
Vídeo complementar do canal
No vídeo tem o Hermes Desktop, um app com interface gráfica pra gerenciar o agente de IA Hermes, rodando local ou em uma VPS
Próximo passo: manter a aplicação no ar
Com a aplicação publicada, o que importa agora é atualizar sem medo
Monte esta rotina pra cada atualização:
- Confirme a data do último backup da VPS antes de mexer em qualquer coisa: a Hostinger faz backups semanais grátis, mas vale garantir que o mais recente já cobre o estado atual do seu projeto
- Rode
npx @astrojs/upgradepra alinhar Astro e adapter - Rebuilde a imagem, publique e reimplante o contêiner
- Abra o domínio de novo e confira o HTTPS e uma rota dinâmica
Quando essa rotina ficar repetitiva, a API de VPS da Hostinger é o caminho pra automatizar deploys e gerenciar a infraestrutura. Mas comece pela rotina manual, beleza?
Se ainda vai montar o ambiente, dá pra conferir a VPS para este projeto e seguir o passo a passo desde o template Docker
Até o próximo post! 🙂
Perguntas frequentes
Dá para gerar a imagem Docker direto na VPS, sem usar outra máquina?
Dá, já que a VPS com o template Docker da Hostinger tem acesso root e docker-ce instalado, então o build pode rodar ali dentro. Pra implantar pelo Docker Manager, como no passo 7, você pode publicar a imagem num registry como o Docker Hub, ou usar as opções "Compose manually" e "Compose from URL", que não exigem ter a imagem publicada antes
O Nginx Proxy Manager substitui a necessidade de configurar allowedDomains no Astro?
Não, são camadas diferentes. O Nginx Proxy Manager cuida do HTTPS e do certificado Let’s Encrypt na frente do contêiner, enquanto security.allowedDomains é quem valida o cabeçalho X-Forwarded-Host dentro do próprio Astro, então os dois precisam estar configurados.
Quer aprender mais sobre inteligência artificial e programação na prática? Conheça o canal Hora de Codar no YouTube e acompanhe os próximos vídeos.
Formações
Formação Claude Code
Domine Claude Code do absoluto zero até o avançado
- 120 aulas
- 4 projetos
- 9h 48min
Blog | Mais populares

Checklist de segurança n8n VPS pública: guia essencial para proteger sua instalação
Checklist de segurança n8n VPS pública: guia essencial para proteger sua instalação A popularidade da automação de processos com o n8n está em alta, principalmente […]

Checklist de segurança n8n VPS 2026: proteja sua automação
Confira o checklist de segurança n8n VPS 2026 e mantenha sua automação protegida. Veja práticas essenciais e atualizadas para seu servidor. Manter seu ambiente de […]
Quanto custa uma VPS? Preço atualizado 2026
Quando se fala em quanto custa uma VPS, é preciso contextualizar o tema a partir de um provedor específico, porque o preço só faz sentido […]
