Como manter uma API Bun rodando na VPS depois de fechar o SSH (com systemd)

Fala aí, beleza? Tu sobe a API Bun na VPS, testa, está tudo funcionando… aí fecha o terminal e a API some junto 😛
Tenha acesso root e SSD NVMe, com 10% OFF extra pelo link da Hora de Codar.
Ver a oferta de VPS Parceria Hora de CodarIsso acontece porque o processo está preso à sessão SSH: quando a sessão morre, ele morre junto
Aqui o caminho é registrar a aplicação como serviço do systemd, seguindo o guia oficial Run Bun as a daemon with systemd da documentação do Bun. Este post fica só nisso: serviço, logs e reinício controlado
O que você precisa antes de criar o serviço?
Você precisa de uma VPS Linux com acesso SSH, do Bun instalado pelo script oficial e de um usuário comum (não root) que seja dono do projeto
Os requisitos do Bun no Linux, segundo a documentação de instalação, são estes:
- o pacote
unzipinstalado - kernel 5.6 ou superior é o recomendado pela documentação; o Bun roda a partir do kernel 3.10 (RHEL 7), com degradação gradual de syscalls mais novas
Pra conferir o kernel:
uname -r
Com isso ok, instale o Bun com o usuário que vai rodar a aplicação:
curl -fsSL https://bun.com/install | bash
O binário fica em ~/.bun/bin. A documentação de instalação avisa que pode ser necessário adicionar esse caminho ao PATH manualmente. Abra um terminal novo e confirme:
bun --version
Se aparecer a versão, beleza 🙂 Se o terminal não reconhecer o comando bun, adicione ~/.bun/bin ao seu PATH (no .bashrc ou .zshrc, por exemplo) e abra um terminal novo de novo
Guarda esse detalhe do ~/.bun/bin, porque ele volta no passo 1
Qual VPS usar para este projeto?
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 é bem prático: a VPS vem com acesso root, que é o que te deixa criar a unit em /etc/systemd/system e mexer no systemctl sem pedir permissão pra ninguém
Além disso, o armazenamento é SSD NVMe, os processadores são AMD EPYC e tem backups semanais gratuitos, o que é uma rede de segurança massa quando uma edição na unit ou no projeto dá ruim
Na Hostinger, tu acessa o servidor de dois jeitos, segundo a documentação de suporte sobre conexão SSH:
- pelo terminal do navegador: no hPanel, vá em VPS > Manage e clique no botão Web Console, no canto superior direito da página de visão geral da VPS
- por um cliente SSH local, usando o usuário, o IP e a porta que aparecem no card de detalhes da VPS, na aba Overview (a porta exibida ali é a que você deve usar, não assuma que é sempre a mesma em todo plano)
Passo a passo: rodar a API Bun como serviço do systemd
A ideia é simples: tu descreve pro systemd quem roda a API, em qual pasta, com qual comando e o que fazer se ela cair. Ele cuida do resto
Pensa nele como um gerente do servidor: tu passa as instruções uma vez e ele mantém a aplicação de pé, mesmo sem ninguém logado
1. Confira o caminho absoluto do Bun e do projeto
O systemd não usa o PATH do seu shell
Ou seja, aquele bun que funciona no seu terminal não existe pra ele. Por isso o serviço precisa do caminho completo do binário, que fica em /home/YOUR_USER/.bun/bin/bun
Pra pegar o caminho absoluto do projeto, entre na pasta dele e rode:
pwd
O resultado esperado é um caminho que começa com /home/YOUR_USER/, e é ele que vai no WorkingDirectory
O erro comum deste passo: escrever só bun run index.ts no ExecStart. O serviço não encontra o executável e não sobe
2. Crie o arquivo my-app.service
Crie o arquivo /etc/systemd/system/my-app.service (com sudo, usando o editor que você já usa na VPS)
E por que nessa pasta? Segundo a man page do systemd.unit, na busca por arquivos de unit, o systemd olha /etc/systemd/system antes de /usr/local/lib/systemd/system e de /usr/lib/systemd/system (onde ficam units instaladas por pacotes). Ou seja, é o lugar certo pra colocar a unit do seu projeto
O conteúdo segue o modelo do guia oficial do Bun
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
O arquivo tem três seções: Unit, Service e Install. Cada uma começa com o próprio nome entre colchetes, numa linha só dela, e as linhas abaixo pertencem àquela seção
Na seção Unit:
Description=My App
After=network.target
Na seção Service:
Type=simple
User=YOUR_USER
WorkingDirectory=/home/YOUR_USER/path/to/my-app
ExecStart=/home/YOUR_USER/.bun/bin/bun run index.ts
Restart=always
Na seção Install:
WantedBy=multi-user.target
Tome cuidado: sem o cabeçalho de cada seção, o systemd não entende o arquivo. O modelo completo, já com os cabeçalhos, está no guia oficial do Bun que linkei lá em cima
Se liga no que cada parte faz:
After=network.target: a API sobe depois que a pilha de gerenciamento de rede inicia, mas isso não garante que a rede já esteja de fato no ar, segundo o próprio guiaUser: o usuário que roda a aplicaçãoWorkingDirectory: a pasta do projetoExecStart: o comando, com o caminho absoluto do Bun (o guia exige caminhos absolutos aqui)Restart=always: se o processo cair, o systemd sobe de novo (mais detalhes sobre essa opção na seção de reinício controlado, mais abaixo)WantedBy=multi-user.target: é o que permite o serviço iniciar no boot quando habilitado
O erro comum deste passo: esquecer de trocar YOUR_USER pelo usuário real (no User e nos caminhos). Dá pra rodar como root trocando por root e /root, mas o próprio guia do Bun não recomenda isso, por segurança
3. Defina a porta (e o cuidado com 80 e 443)
Quando a opção port não é definida no código, o Bun.serve lê as variáveis BUN_PORT, PORT e NODE_PORT (ou a flag --port). Se nenhuma estiver definida, ele usa a 3000, conforme a documentação do servidor HTTP do Bun
Com a unit acima, sem nenhuma variável, a API escuta na 3000
Agora, tome cuidado: por padrão, usuário sem root não escuta nas portas 80 ou 443. Pra liberar isso de forma permanente, o guia do Bun usa o setcap no caminho absoluto do binário, com sudo:
sudo setcap CAP_NET_BIND_SERVICE=+eip /home/YOUR_USER/.bun/bin/bun
Troque YOUR_USER pelo usuário real da aplicação. Se o serviço roda como root, esse passo não é necessário
Um detalhe pra manutenção: trocar o binário do Bun (por exemplo, com bun upgrade) remove essa capability. Depois de atualizar, rode o setcap de novo, ou então use AmbientCapabilities=CAP_NET_BIND_SERVICE na seção Service da unit
O erro comum deste passo: configurar a API na porta 80 com usuário comum, sem o setcap, e ela não subir
4. Recarregue o systemd, habilite e inicie
Com o arquivo salvo, é hora de avisar o systemd:
sudo systemctl daemon-reload
sudo systemctl enable my-app
sudo systemctl start my-app
O enable faz o serviço subir sozinho no próximo reboot da VPS
Já o start inicia a API agora, sem precisar reiniciar a máquina
E quando você editar a unit de novo, o passo é sempre o mesmo: edita o arquivo e roda sudo systemctl daemon-reload antes de reiniciar o serviço

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.O erro comum deste passo: editar a unit e não rodar daemon-reload. O systemd continua usando a versão antiga do arquivo e tu fica sem entender por que a mudança não pegou
5. Valide: feche o SSH e volte
Primeiro, confira o estado do serviço:
systemctl status my-app
O status mostra o estado do serviço (a linha Active aparece como active, inactive ou failed) e as linhas mais recentes do journal
Agora a prova real: feche a sessão SSH, conecte de novo e rode o mesmo status
Se ele continua ativo, a API não depende mais do seu terminal. Missão cumprida 😀
Como ver os logs da API Bun com journalctl?
Os logs ficam no journal do systemd, e você lê com journalctl
Por padrão, o StandardOutput de um serviço usa o DefaultStandardOutput, que é o journal (está na documentação do systemd.exec). Então tudo que a API escreve no stdout cai lá
Pra filtrar só a sua aplicação:
journalctl -u my-app
Pra acompanhar em tempo real (tipo um tail):
journalctl -u my-app -f
O -u filtra pela unit e o -f vai mostrando as entradas novas conforme chegam
Um detalhe se você usa um usuário comum: por padrão, só o root e membros de alguns grupos (como systemd-journal, adm e wheel) conseguem ler o journal do sistema. Se o journalctl não mostrar nada, pode ser isso
Checagem: deixe o journalctl -u my-app -f aberto, faça uma requisição numa rota que tenha console.log e veja a mensagem aparecer
O erro comum aqui: procurar um arquivo de log na pasta do projeto. Por padrão ele não existe, o log está no journal
Reinício controlado: Restart=always, on-failure e RestartSec
A escolha entre always e on-failure define quando o systemd vai religar a sua API
Restart=always(o que vem no guia do Bun): reinicia o processo toda vez que ele termina, cobrindo mais cenários de queda do que oon-failureRestart=on-failure: reinicia quando o processo sai com código diferente de zero, é encerrado por sinal, estoura timeout de uma operação ou dispara o watchdog, segundo a man page do systemd.service
Se você quer que a API possa encerrar de propósito sem voltar sozinha, troque a linha Restart=always na seção Service da unit por:
Restart=on-failure
E o intervalo entre as tentativas? Sem configuração, o RestartSec é de 100ms, segundo a man page do systemd.service
É bem rápido… Se a API quebra logo ao iniciar, ela entra num ciclo de cai e sobe colado, enchendo o journal. Aumentar o RestartSec dá um respiro entre as tentativas e deixa o log mais fácil de ler
Um cuidado com Type=simple (o tipo usado no modelo do guia): o systemctl start pode informar sucesso mesmo quando o binário não consegue ser executado, por exemplo se o User estiver errado ou o caminho do Bun estiver errado. Por isso o status e o journalctl são o jeito confiável de confirmar que a API realmente subiu
Lembra: mexeu na unit, roda sudo systemctl daemon-reload antes de reiniciar
Fluxo de deploy com systemctl restart
Na hora de atualizar a API, a rotina fica assim:
- atualize o código na pasta do projeto
- rode
sudo systemctl restart my-app - confira com
systemctl status my-app - acompanhe com
journalctl -u my-app -fpra ver se subiu sem erro
O erro comum aqui: o serviço fica reiniciando em loop por causa de um erro no código, e a pessoa conclui que o problema é o servidor. Não é! O motivo está no journalctl -u my-app, e a correção é no código ou na unit, não em trocar de VPS
Próximo passo depois que a API está no ar
Faça um reboot de teste da VPS
Quando ela voltar, rode systemctl status my-app sem iniciar nada na mão. Se o serviço estiver ativo, o enable fez o trabalho dele e a API sobrevive a reinício do servidor
Depois, transforma o fluxo restart + status + journalctl em rotina fixa de deploy. Fica previsível e tu para de ficar no escuro quando algo quebra
E se você ainda não tem servidor, dá pra conferir a VPS para este projeto: os backups semanais gratuitos ajudam a recuperar o projeto caso alguma alteração dê ruim
Até o próximo post! 🙂
Perguntas frequentes
Dá pra rodar mais de uma API Bun na mesma VPS com systemd?
Dá sim. Cada projeto ganha seu próprio arquivo de unit, com nome, WorkingDirectory e ExecStart apontando pra pasta e pro index.ts daquele projeto. O systemd trata cada unit de forma independente, então o enable, o start e o journalctl -u também são feitos um por um.
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 […]
