Como hospedar SvelteKit em uma VPS com adapter-node e Docker

Fala aí, beleza? O SvelteKit 3 saiu oficialmente em 1º de outubro de 2026, e muito tutorial de deploy que tá por aí simplesmente parou de funcionar 😛
Tenha acesso root e SSD NVMe, com 10% OFF extra pelo link da Hora de Codar.
Ver a oferta de VPS Parceria Hora de CodarEm 01/11/2026, a versão mais recente do @sveltejs/kit é a 3.1.1, lançada em 29/10/2026. A versão mais recente do @sveltejs/adapter-node é a 6.0.1, lançada na mesma data: um patch que corrige um bug de resposta text/event-stream com headers imutáveis e já declara dependência do kit 3.1.1
As mudanças estruturais que quebram guia antigo vieram do SvelteKit 3 e do adapter-node 6.0.0, os dois lançados em 01/10/2026: o guia de migração oficial do SvelteKit 3 documenta que o svelte.config.js não é mais suportado, e o changelog do adapter-node 6.0.0 registra que ele agora exige SvelteKit 3 e remove a variável de ambiente ORIGIN
Aqui o recorte é bem específico: projeto SvelteKit 3, servidor Node gerado pelo adapter, container Docker numa VPS e um proxy reverso na frente. Tudo com base na documentação oficial do adapter-node, bora?
O que você precisa antes de fazer o deploy do SvelteKit na VPS
Antes de tudo, o projeto precisa estar no SvelteKit 3, porque o adapter-node 6 exige essa versão
As versões mínimas pedidas pelo SvelteKit 3, segundo o guia de migração oficial, são:
- Node 22.17
- TypeScript 6
- Svelte 5.57.1
- Vite 8.0.12
@sveltejs/vite-plugin-svelte7
Se o projeto ainda tá no SvelteKit 2, a migração vem primeiro (já já mostro o comando)
Do lado do servidor, você precisa de uma VPS com Docker e acesso pra administrar o ambiente
Pra esse cenário, a recomendação principal da Hora de Codar é a VPS da Hostinger (este artigo contém links de afiliado da Hostinger)
O motivo é técnico, ligado direto à tarefa:
- Acesso root: você instala e administra o que precisar no servidor
- Template Docker: template Ubuntu 24.04 com
docker-ceedocker-composepré-instalados - Docker Manager: interface web no painel, disponível nos planos VPS com template Docker, pra subir compose sem depender só do terminal
- Infra: virtualização KVM, armazenamento SSD NVMe e processadores AMD EPYC
- Firewall: gerenciamento de firewall pelo painel, que vai ser útil pra fechar a porta do container
Vale lembrar: a documentação da Hostinger descreve o Docker Manager como um recurso self-managed: ou seja, subir e administrar os containers pelo compose, seja pelo painel ou por SSH, fica por sua conta, que é exatamente o que os passos abaixo mostram
Como configurar o adapter-node no SvelteKit 3
A configuração do adapter agora mora no vite.config.js (ou .ts), como opção do plugin sveltekit()
Nada de svelte.config.js, beleza? Bora no passo a passo:
- Migre o projeto, se ainda estiver no SvelteKit 2. Rode o comando oficial:
npx sv migrate sveltekit-3
Ele faz a parte mecânica e deixa uma lista de pendências, então leia essa lista com calma antes de seguir
- Instale o adapter-node como dependência de desenvolvimento:
npm i -D @sveltejs/adapter-node
- Configure o adapter no plugin
sveltekit(), importando de@sveltejs/kit/vitee do pacote do adapter:
// vite.config.ts
import { defineConfig } from 'vite';
import { sveltekit } from '@sveltejs/kit/vite';
import adapter from '@sveltejs/adapter-node';
export default defineConfig({
plugins: [
sveltekit({
adapter: adapter(),
paths: {
origin: 'https://seudominio.com.br'
}
})
]
});
- Defina
paths.origincom a URL pública do app. Antes, a origem era controlada pela variável de ambienteORIGIN(e, pra prerender, pela opçãokit.prerender.origin). No adapter-node 6, isso foi substituído pela opçãopaths.origin, que agora mora direto nosveltekit()dovite.config, como opção de primeiro nível do plugin, e não mais dentro dekit.*
E por que isso importa? O guia de migração do SvelteKit 3 explica que paths.origin deve refletir a origem pública do app quando ela não pode ser derivada com confiança dos cabeçalhos da requisição, que é exatamente o caso atrás de proxy reverso. É esse mesmo valor que entra na checagem de CSRF de envio de formulários e chamadas de remote functions
Um detalhe que vale ter em mente: como essa opção é lida pelo Vite durante o npm run build, a expectativa é que, ao mudar o domínio, seja preciso gerar um build (e uma imagem Docker) novos pra que o valor atualize: a documentação não detalha esse comportamento de cache especificamente, mas é o que se espera de uma opção resolvida em tempo de build
Erro comum deste passo: copiar de tutorial antigo o svelte.config.js ou a variável ORIGIN no ambiente. Nenhum dos dois funciona no adapter-node 6
Resultado esperado: um vite.config válido, com o adapter e a origem dentro do sveltekit() 🙂
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
Como gerar o build e separar as dependências de produção
O npm run build gera o servidor de produção no diretório de saída do adapter, que por padrão é build
Só que essa pasta sozinha não roda nada
Pra subir em produção, você precisa de três coisas:
- o diretório de saída (
build) - o
package.jsondo projeto - as dependências de produção em
node_modules
As dependências de produção saem copiando o package.json e o package-lock.json e rodando:
npm ci --omit dev
Agora a decisão que mais pega gente desprevenida: onde colocar cada pacote
- devDependencies: o pacote é empacotado dentro do app com Rolldown
- dependencies: o pacote fica externo e, como regra geral, PRECISA existir em
node_modulesno servidor
É meio contraintuitivo, né? Mas faz sentido: o que tá em dependencies o bundle deixa de fora e espera achar instalado na hora de rodar
Tem uma exceção que vale guardar: o changelog do adapter-node 6 registra que bibliotecas Svelte listadas em dependencies são empacotadas (bundled) mesmo assim, em vez de externalizadas. Então a regra "dependencies = externo" vale pra dependências em geral, não pra bibliotecas Svelte
Pra checar localmente, sobe o servidor:
node build
Por padrão ele aceita conexões em 0.0.0.0, na porta 3000
Erro comum deste passo: colocar em dependencies um pacote (que não seja biblioteca Svelte) sem ele estar instalado na imagem. O servidor falha ao importar o módulo assim que tenta usar ele
E mais uma pegadinha do adapter-node 6: a lista de assets estáticos é registrada no build
Arquivo jogado na pasta de saída depois do build não é servido, então se precisar de um asset novo, gere o build de novo
Dockerfile e docker compose para SvelteKit com adapter-node
O Dockerfile abaixo segue o que a doc do adapter-node pede pra rodar em produção: o diretório de saída, o package.json e as dependências de produção em node_modules
A ideia é um build multi-stage: um estágio compila, o outro só roda
# Estágio de build
FROM node:22 AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# Estágio final (Node 22.17 ou superior)
FROM node:22
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit dev
COPY --from=build /app/build ./build
EXPOSE 3000
CMD ["node", "build"]
O SvelteKit 3 exige Node 22.17 ou superior, então confira a versão da imagem com docker run --rm node:22 node --version antes de confiar na tag
Agora um compose simples, que publica a porta 3000 só no localhost do servidor, pra ela ser acessada pelo proxy e não direto pela internet:
services:
app:
build: .
ports:
- "127.0.0.1:3000:3000"
environment:
PORT: "3000"
Se quiser outra porta, o servidor aceita HOST e PORT, como no exemplo da doc: HOST=127.0.0.1 PORT=4000 node build
Dentro do container, porém, o padrão 0.0.0.0 é o que deixa o Docker encaminhar a porta publicada, então mexa no HOST só se souber o motivo
E se usar SOCKET_PATH, o HOST e o PORT são ignorados
Pra quem já subiu algo parecido, a estrutura é a mesma de outros guias do blog, tipo o de Directus com Docker Compose, só que aqui com um único serviço
Na Hostinger, tem dois caminhos pra subir o compose:
- Pelo painel: painel da VPS > Docker Manager > Compose, usando "Compose manually" ou "Compose from URL" (guia do Docker Manager). Se for usar esse caminho com um compose que faz
build: ., confirme antes na documentação oficial se o Docker Manager builda a partir do código do projeto ou se espera uma imagem já publicada; na dúvida, o caminho mais direto pra build local é por SSH, como abaixo - Por SSH: no template Ubuntu 24.04, que já vem com
docker-compose, rode dentro da pasta do projeto:

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.docker-compose up -d
Erro comum deste passo: copiar só a pasta build pra imagem e esquecer o node_modules de produção. O servidor até inicia o processo, mas quebra ao importar o primeiro pacote externo
Como configurar o SvelteKit atrás de proxy reverso (HTTPS e origem pública)
Isso importa por causa da origem
Sem paths.origin, o SvelteKit deriva a origem do cabeçalho host da requisição e usa o protocolo https por padrão, a menos que a variável PROTOCOL_HEADER esteja definida apontando pra outro cabeçalho. Essa origem entra na checagem de CSRF de formulários e remote functions
Por isso, se a origem derivada (ou configurada) não corresponder à URL pública real do app, essa checagem pode falhar: daí a importância de acertar paths.origin ou, quando fizer sentido, os cabeçalhos repassados pelo proxy
Tu tem duas saídas:
paths.originno build: a preferível quando o domínio é fixo, porque não depende de nenhum cabeçalho ser repassado corretamente pelo proxy- Cabeçalhos do proxy: o adapter-node tem variáveis pra isso, incluindo a já citada
PROTOCOL_HEADER. Pra host, porta, IP do cliente e limite de tamanho do corpo da requisição (upload), confira os nomes exatos, a semântica e os valores padrão direto na documentação oficial do adapter-node antes de aplicar, porque esses detalhes variam entre versões e prefiro não arriscar um nome ou valor errado aqui
Importante: cabeçalho HTTP pode ser manipulado por quem faz a requisição. Então qualquer variável que diga "leia o host, o protocolo ou o IP de tal cabeçalho" só é segura se existir, de fato, um proxy confiável na frente sobrescrevendo esse cabeçalho antes dele chegar no container. Se a porta do container estiver exposta direto pra internet, sem proxy nenhum, não configure esse tipo de variável
Erro comum deste passo: expor a porta do container direto na internet com variáveis de cabeçalho de proxy configuradas. Isso faz o servidor confiar em cabeçalhos que o próprio cliente pode estar enviando, então só configure elas atrás de um proxy que você controla
A configuração do proxy em si (Nginx, Caddy e etc.) foge do escopo deste guia, mas o raciocínio de container atrás de proxy com HTTPS aparece no guia de aplicação Bun com HTTPS, que serve de referência pro desenho geral
Como verificar se as rotas e formulários funcionam em produção
Com tudo no ar, a checagem é pelo domínio público, não pelo IP nem pelo localhost
- Página inicial e rota dinâmica: abra a home e uma rota com parâmetro (tipo uma página de post ou produto) e confira se as duas renderizam
- Asset estático: pegue um arquivo da pasta
statice rodecurl -I https://seudominio.com.br/arquivo.png. No adapter-node 6, estáticos são servidos só pra GET e HEAD e validados porETagcom hash de conteúdo, semLast-Modified, então o esperado é ver oETagna resposta - Formulário POST: envie um formulário de verdade pelo navegador. Se passar, a checagem de CSRF aceitou a origem
- IP do cliente: se você configurou alguma variável de proxy pra leitura do IP, confira (num log ou rota de debug) se o IP lido é o do cliente e não o do proxy
Pra manutenção, vale guardar a expectativa: mudou o domínio ou adicionou um asset novo, o caminho mais seguro é gerar build e imagem novos, já que a lista de assets estáticos é registrada em tempo de build e paths.origin também é lido nesse momento
Na Hostinger, use o firewall do painel pra deixar abertas só as portas do proxy
Os backups semanais com restauração pelo painel ajudam, mas não substituem versionar o código e o arquivo do compose no Git
Erros comuns ao hospedar SvelteKit com adapter-node e como resolver
- A variável
ORIGINnão tem efeito > foi removida no adapter-node 6 > usepaths.originnovite.confige faça novo build > revise tutoriais antigos antes de copiar - Checagem de CSRF falha em formulário atrás de proxy > a origem derivada não bate com a URL pública > defina
paths.origin(preferível) ou confira na documentação oficial do adapter-node quais cabeçalhos de proxy ajustar > teste um formulário a cada deploy - Configuração ignorada > ainda está no
svelte.config.js> mova pra dentro dosveltekit()novite.config> rodenpx sv migrate sveltekit-3e leia as pendências - Arquivo estático novo retorna 404 > foi adicionado depois do build > gere o build de novo > nunca edite a pasta de saída na mão
- Upload grande rejeitado > o adapter-node limita por padrão o tamanho do corpo da requisição (variável
BODY_SIZE_LIMIT) > confira o valor padrão atual e como ajustar na documentação oficial do adapter-node > defina o limite junto com a regra de negócio do upload - Falha ao importar pacote no container > o pacote está em
dependencies(e não é biblioteca Svelte) e a imagem não rodounpm ci --omit dev> inclua a etapa no Dockerfile > confira o estágio final da imagem
Repara que nenhum desses erros se resolve trocando de servidor
São todos de configuração, então a correção é no projeto, no Dockerfile ou no compose 😉
Vídeo de apoio
No canal da Hora de Codar tem um vídeo sobre o Hermes Desktop App, um gerenciador remoto/local para o Hermes Agent, pra quem quiser conhecer essa ferramenta
Próximo passo: colocar o SvelteKit no ar
Com o projeto migrado pro SvelteKit 3 e o paths.origin apontando pro domínio certo, o próximo movimento é escolher o servidor
Dá pra conferir a VPS para este projeto, aplicar o template Docker com Ubuntu 24.04 e subir o compose pelo Docker Manager ou por SSH
Depois disso, falta só o proxy reverso com HTTPS na frente do container e a checagem das rotas pelo domínio público
Até o próximo post! 🙂
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 […]
