---
title: "Como configurar CI/CD na VPS com deploy automático"
description: "Entenda CI, entrega e deploy contínuo e monte na VPS uma arquitetura simples: pipeline, artefato, releases com symlink, health check e rollback."
url: "https://streethosting.com.br/guias/vps/configurar-ci-cd-vps"
category: "vps"
slug: "configurar-ci-cd-vps"
datePublished: "2026-09-28"
dateModified: "2026-09-28"
author: "Equipe StreetHosting"
difficulty: "intermediario"
language: "pt-BR"
keywords:
  - "configurar ci cd vps"
  - "o que e ci cd"
  - "deploy continuo vps"
  - "rollback deploy symlink"
  - "pipeline deploy servidor"
  - "entrega continua"
---

# CI/CD na VPS: do conceito a um pipeline com rollback

CI/CD não exige Kubernetes nem ferramenta cara. Com um pipeline, uma pasta de releases e um health check, uma VPS comum publica cada versão com segurança e volta atrás em segundos quando algo quebra.

> **Resposta rápida**
>
> **CI/CD na VPS** é automatizar o caminho do commit até a produção: a integração contínua compila e testa cada mudança, e a entrega ou o deploy contínuo publica o resultado no servidor. A arquitetura mais simples que funciona tem um pipeline que gera um artefato, uma pasta de releases na VPS com um symlink para a versão ativa, um health check depois do reinício e rollback trocando o symlink de volta.

## CI, CD e deploy contínuo

As três expressões aparecem juntas e muitas vezes como sinônimos, mas cada uma descreve até onde a automação vai.

| Prática | O que é automático | Onde para | Quando usar |
| --- | --- | --- | --- |
| Integração contínua (CI) | Build, lint e testes a cada push ou pull request | No resultado verde ou vermelho | Sempre, até em projeto de uma pessoa só |
| Entrega contínua (CD) | CI mais um artefato pronto, publicado em homologação | Em uma aprovação humana antes da produção | Equipes que querem escolher o horário da publicação |
| Deploy contínuo (CD) | Tudo acima, incluindo a produção | Não para: passou nos testes, está no ar | Projetos com bons testes e rollback rápido |

Na prática, o CD da sigla pode ser qualquer um dos dois últimos. O que importa é a regra por trás: nada chega à produção sem passar pelo mesmo caminho automático, e ninguém edita arquivo direto no servidor. Correção feita à mão pelo SSH faz a produção divergir do repositório, e o próximo deploy apaga a correção sem ninguém perceber.

Deploy contínuo só é seguro com duas redes de proteção: testes que pegam o grosso dos erros antes da publicação e um jeito de voltar atrás em segundos quando algo escapa. O restante deste guia monta a segunda rede dentro de uma VPS comum.

## A arquitetura mínima

Cinco peças bastam para a maioria dos projetos que rodam em uma única VPS:

- **Repositório:** a única fonte de verdade. A branch main representa o que está em produção.
- **Pipeline:** roda fora da VPS, no GitHub Actions, no GitLab CI ou em outro serviço. Instala dependências, testa e gera o build.
- **Artefato ou imagem:** o resultado empacotado e imutável de um commit, como um arquivo .tar.gz ou uma imagem Docker marcada com o hash do commit. O mesmo pacote que passou nos testes é o que vai para produção, sem novo build no caminho.
- **VPS:** recebe o artefato, descompacta em uma pasta nova e troca a versão ativa.
- **Health check e rollback:** depois do reinício, um endpoint de saúde diz se a nova versão está viva. Se não responder, o script volta sozinho para a versão anterior.

Repare no que ficou de fora: compilar dentro da VPS, rodar git pull em produção, instalar dependências de desenvolvimento no servidor. Tudo isso aumenta o tempo em que a aplicação fica em estado inconsistente. A conexão entre pipeline e servidor, com usuário de deploy sem root, secrets e envio por rsync, está detalhada no guia de [deploy com GitHub Actions na VPS](https://streethosting.com.br/guias/vps/deploy-github-actions-vps). Aqui o foco é o que acontece dentro do servidor.

## Releases em diretórios com symlink

A técnica mais antiga e confiável de deploy em servidor único é nunca sobrescrever a versão que está rodando. Cada deploy ganha uma pasta própria, e um symlink chamado `current` aponta para a ativa:

```
/srv/minha-app/
  releases/
    20260927101500/
    20260928143000/
  shared/
    .env
    uploads/
  current -> releases/20260928143000
  deploy.sh
```

O serviço aponta sempre para o caminho fixo `/srv/minha-app/current`. Publicar é descompactar em uma pasta nova e mudar o symlink; voltar atrás é apontar o symlink para a pasta anterior. Nenhum arquivo da versão antiga é tocado, então o rollback não depende de build nem de download.

A pasta `shared` guarda o que sobrevive entre versões: variáveis de ambiente, uploads de usuários, arquivos de cache persistente. Cada release recebe links para ela, e assim nenhum deploy apaga dado de produção. O serviço do systemd fica assim:

```
[Service]
User=deploy
WorkingDirectory=/srv/minha-app/current
ExecStart=/usr/bin/node /srv/minha-app/current/dist/server.js
Restart=on-failure
```

O caminho do symlink é resolvido quando o processo inicia, então o reinício é o instante exato em que a versão nova entra no ar. Até lá, a anterior continua atendendo normalmente.

## Script com health check e rollback

O pipeline envia o pacote para a VPS e chama um script que faz o resto. Salve em `/srv/minha-app/deploy.sh`, com dono deploy e permissão de execução (`chmod +x`):

```
#!/usr/bin/env bash
set -euo pipefail

APP=/srv/minha-app
PACOTE=/tmp/minha-app.tar.gz
RELEASE=$(date +%Y%m%d%H%M%S)
NOVA="$APP/releases/$RELEASE"
ANTERIOR=$(readlink -f "$APP/current" || true)

mkdir -p "$NOVA"
tar -xzf "$PACOTE" -C "$NOVA"
ln -s "$APP/shared/.env" "$NOVA/.env"
ln -s "$APP/shared/uploads" "$NOVA/uploads"
cd "$NOVA" && npm ci --omit=dev

ativar() {
  ln -sfn "$1" "$APP/current.tmp"
  mv -T "$APP/current.tmp" "$APP/current"
  sudo systemctl restart minha-app.service
}

ativar "$NOVA"

for tentativa in $(seq 1 10); do
  if curl -fsS http://127.0.0.1:3000/health > /dev/null; then
    echo "Release $RELEASE no ar"
    ls -1dt "$APP"/releases/* | tail -n +6 | xargs -r rm -rf
    exit 0
  fi
  sleep 3
done

echo "Health check falhou"
if [ -n "$ANTERIOR" ] && [ -d "$ANTERIOR" ]; then
  echo "Voltando para $ANTERIOR"
  ativar "$ANTERIOR"
fi
exit 1
```

- **Troca atômica:** o link novo nasce com nome temporário e o `mv -T` o renomeia por cima do antigo em uma única operação. Em nenhum instante `current` deixa de existir.
- **Janela de subida:** o script tenta o health check dez vezes, a cada três segundos. Ajuste ao tempo real de inicialização da sua aplicação.
- **Limpeza:** depois de um deploy saudável, só as cinco releases mais recentes ficam no disco. Cinco versões de rollback instantâneo cobrem quase todo incidente.
- **Código de saída:** se o health check falha, o script volta para a versão anterior e sai com erro. O job do pipeline fica vermelho, e você fica sabendo sem olhar o servidor.

O comando de reinício precisa de uma regra no sudoers liberando só ele para o usuário de deploy, como mostra o guia de GitHub Actions. No workflow, os dois passos finais ficam assim:

```
      - name: Empacotar
        run: tar -czf minha-app.tar.gz dist package.json package-lock.json

      - name: Enviar e publicar
        run: |
          scp -i ~/.ssh/deploy_key minha-app.tar.gz "$DEST:/tmp/minha-app.tar.gz"
          ssh -i ~/.ssh/deploy_key "$DEST" /srv/minha-app/deploy.sh
```

### O que o health check deve testar

Um endpoint `/health` que responde 200 só quando a aplicação consegue atender de verdade: conectou no banco, leu a configuração, carregou o que precisa. Um health check que sempre responde 200 só prova que o processo existe. Fora do deploy, o mesmo endpoint alimenta um monitor externo, como o [Uptime Kuma](https://streethosting.com.br/guias/vps/monitoramento-vps-com-uptime-kuma), que avisa quando a produção cai entre um deploy e outro.

> **Atenção**
>
> Rollback de código não desfaz migração de banco. Se a versão nova alterou o esquema, a antiga pode não funcionar com ele. Escreva migrações compatíveis com as duas versões: primeiro acrescente colunas e tabelas, publique o código que as usa, e só remova o que ficou obsoleto em um deploy posterior.

## Artefato ou imagem Docker

O pacote .tar.gz funciona para qualquer linguagem interpretada e para binários compilados, e é o caminho com menos peças. A alternativa é empacotar a aplicação em uma imagem Docker, que leva junto o runtime e as bibliotecas do sistema. O pipeline faz o build da imagem, marca com o hash do commit e envia para um registry, como o do próprio GitHub. Na VPS, o compose referencia a tag:

```
services:
  app:
    image: ghcr.io/sua-org/minha-app:${TAG}
    restart: unless-stopped
    env_file: .env
    ports:
      - "127.0.0.1:3000:3000"

# publicar ou voltar para uma versão é trocar a tag:
# TAG=a1b2c3d docker compose pull && TAG=a1b2c3d docker compose up -d
```

- **Rollback:** subir de novo com a tag do commit anterior. A imagem antiga ainda está no disco ou no registry.
- **Credencial:** a VPS faz login no registry com um token que só tem permissão de leitura de pacotes.
- **Health check:** continua necessário. O comando de subida retorna antes de a aplicação estar pronta, então use o mesmo laço com curl depois dele.
- **Porta:** publique em 127.0.0.1 e coloque o Nginx na frente, porque porta publicada pelo Docker ignora as regras do UFW. A configuração está em [Nginx como reverse proxy na VPS](https://streethosting.com.br/guias/vps/configurar-nginx-reverse-proxy-vps).

Imagem compensa quando a aplicação depende de bibliotecas do sistema, quando há vários serviços para coordenar ou quando você quer o mesmo ambiente no notebook e no servidor. Se ainda não tem Docker na VPS, comece por [instalar Docker no Ubuntu](https://streethosting.com.br/guias/vps/instalar-docker-ubuntu-vps).

## Webhook, runner self hosted e Coolify

Pipeline externo conectando por SSH é o modelo mais comum, mas não o único. A escolha muda quem inicia o deploy e o que precisa ficar aberto na VPS.

| Abordagem | Quem inicia | Acesso exigido na VPS | Bom para |
| --- | --- | --- | --- |
| Pipeline externo por SSH | Runner do GitHub ou do GitLab | Porta SSH aberta, login só por chave | A maioria dos projetos em VPS única |
| Webhook | A própria VPS, ao receber um aviso do Git | Endpoint HTTPS protegido por segredo | Quem não quer credencial SSH fora do servidor |
| Runner self hosted | Um agente na VPS que busca jobs no GitHub | Nenhuma porta de entrada, só conexão de saída | Builds pesados e repositórios privados |
| Coolify | Painel instalado na VPS e integrado ao Git | Portas 80 e 443 e acesso ao painel | Várias aplicações com interface e SSL automático |

### Webhook

O GitHub envia um POST a cada push. Um pequeno serviço na VPS valida a assinatura HMAC que chega no cabeçalho `X-Hub-Signature-256`, confere a branch e roda o mesmo `deploy.sh`. Sem validar a assinatura, qualquer pessoa que descubra a URL dispara deploys. Nesse modelo, o script baixa um artefato pronto ou faz o build na própria VPS.

### Runner self hosted

O agente do GitHub Actions instalado na VPS abre uma conexão de saída e executa os jobs localmente. Não exige porta aberta e deixa o build perto do destino. O GitHub recomenda usar runners próprios só com repositórios privados, porque em repositório público um pull request de terceiros pode executar código na sua máquina. Rode o agente com usuário sem root e lembre que, durante o build, ele disputa CPU com a aplicação.

### Coolify

Plataforma open source que você instala na VPS e que faz o papel de um PaaS: conecta no repositório, gera o build a partir do código ou do Dockerfile, publica em containers, emite certificado e reimplanta a cada push. É o caminho para quem prefere configurar por interface a escrever pipeline. A documentação pede no mínimo 2 núcleos e 2 GB de RAM, e builds de verdade pedem mais. O passo a passo está em [instalar Coolify em uma VPS](https://streethosting.com.br/guias/vps/instalar-coolify-vps). Quem quer tirar também o repositório de serviços externos pode hospedar o Git com o [Gitea na própria VPS](https://streethosting.com.br/guias/vps/instalar-gitea-vps), que tem um sistema de Actions com sintaxe compatível com a do GitHub.

## Qual VPS para CI/CD

O tamanho da VPS depende de onde o build acontece. Compilação é trabalho de CPU em rajada: instala dependências, transpila, empacota. Quanto mais alto o clock, menos tempo o deploy leva e menos tempo a aplicação divide o processador com ele.

- **Build fora da VPS, no pipeline:** o servidor só roda a aplicação. A VPS Ryzen 9 9950X com 2 vCPU, 4 GB DDR5 e 40 GB NVMe, por R$ 64,00, atende uma API ou site Node com folga. Para economizar, a VPS Xeon com 3 vCPU e 4 GB sai por R$ 40,00.
- **Build na VPS, com runner self hosted ou Coolify:** a VPS Ryzen com 4 vCPU, 8 GB DDR5 e 80 GB NVMe, por R$ 114,00, é um bom ponto de partida. Com várias aplicações e builds frequentes, 6 vCPU e 16 GB por R$ 214,00 evitam que um deploy deixe as outras lentas.
- **Muitas releases e imagens guardadas:** conte o disco. Cinco releases de uma aplicação Node com dependências, mais imagens Docker antigas, ocupam gigabytes rápido. Os planos Ryzen vão de 20 GB a 640 GB de NVMe.

A linha [VPS Ryzen](https://streethosting.com.br/vps/ryzen) roda em Ryzen 9 9950X com clock de até 5,7 GHz, memória DDR5, NVMe e uplink de 1 Gbps, em São Paulo e com AntiDDoS incluso. Se o volume de builds crescer, o upgrade é feito pelo painel, cobra só a diferença proporcional ao ciclo e exige apenas um reinício da VM. Todas as opções, incluindo a linha Xeon, estão na [página de VPS](https://streethosting.com.br/vps).

> **Dica**
>
> Separar ambientes não exige outra máquina logo de cara. Uma segunda pasta de releases com outro serviço e outra porta já serve de homologação. Quando o tráfego de produção justificar, mova a homologação para uma VPS pequena e mantenha o mesmo script nas duas.

## Perguntas frequentes

### Qual a diferença entre CI e CD?

CI, integração contínua, é compilar e testar automaticamente cada mudança enviada ao repositório. CD pode significar entrega contínua, quando o artefato fica pronto e a publicação em produção espera uma aprovação, ou deploy contínuo, quando tudo que passa nos testes vai direto para produção.

### Preciso de Kubernetes para ter CI/CD?

Não. Uma VPS com uma pasta de releases, um symlink e um script com health check já entrega deploy automático com rollback em segundos. Kubernetes resolve outro problema, o de orquestrar muitos containers espalhados em vários servidores.

### Como fazer rollback de um deploy na VPS?

Com releases em diretórios separados, basta apontar o symlink current para a pasta anterior e reiniciar o serviço, o que leva segundos. Com imagens Docker, o equivalente é subir de novo a tag do commit anterior. Lembre que voltar o código não desfaz migrações já aplicadas no banco.

### É seguro usar runner self hosted na VPS?

É razoável em repositório privado, com o agente rodando sob um usuário sem root e sem acesso a dados sensíveis além do necessário. Em repositório público, o próprio GitHub desaconselha, porque pull requests de terceiros podem acabar executando código no seu servidor.

### Coolify substitui um pipeline de CI/CD?

Para muitos projetos, sim: ele faz o build, publica em containers, emite o certificado e reimplanta a cada push. Quem já tem uma suíte de testes importante costuma manter o CI no GitHub Actions e deixar o Coolify cuidando só da publicação.

## Guias relacionados

- [Deploy com GitHub Actions na VPS: pipeline passo a passo](https://streethosting.com.br/guias/vps/deploy-github-actions-vps.md)
- [Como instalar Coolify na VPS e fazer deploy com HTTPS](https://streethosting.com.br/guias/vps/instalar-coolify-vps.md)
- [Como instalar Docker no Ubuntu 22.04 ou 24.04 em VPS (guia direto ao ponto)](https://streethosting.com.br/guias/vps/instalar-docker-ubuntu-vps.md)
- [Como instalar Uptime Kuma na VPS e monitorar sites e portas](https://streethosting.com.br/guias/vps/monitoramento-vps-com-uptime-kuma.md)

## Produtos citados

- https://streethosting.com.br/vps/ryzen
- https://streethosting.com.br/vps
- https://streethosting.com.br/vps/xeon

## Dados estruturados

```json
[
  {
    "@context": "https://schema.org",
    "@type": [
      "Article",
      "TechArticle"
    ],
    "headline": "CI/CD na VPS: do conceito a um pipeline com rollback",
    "name": "Como configurar CI/CD na VPS com deploy automático",
    "abstract": "CI/CD não exige Kubernetes nem ferramenta cara. Com um pipeline, uma pasta de releases e um health check, uma VPS comum publica cada versão com segurança e volta atrás em segundos quando algo quebra.",
    "description": "Entenda CI, entrega e deploy contínuo e monte na VPS uma arquitetura simples: pipeline, artefato, releases com symlink, health check e rollback.",
    "datePublished": "2026-09-28",
    "dateModified": "2026-09-28",
    "author": {
      "@type": "Organization",
      "name": "Equipe StreetHosting"
    },
    "publisher": {
      "@type": "Organization",
      "name": "StreetHosting",
      "url": "https://streethosting.com.br"
    },
    "inLanguage": "pt-BR",
    "mainEntityOfPage": {
      "@type": "WebPage",
      "@id": "https://streethosting.com.br/guias/vps/configurar-ci-cd-vps"
    }
  },
  {
    "@context": "https://schema.org",
    "@type": "FAQPage",
    "mainEntity": [
      {
        "@type": "Question",
        "name": "Qual a diferença entre CI e CD?",
        "acceptedAnswer": {
          "@type": "Answer",
          "text": "CI, integração contínua, é compilar e testar automaticamente cada mudança enviada ao repositório. CD pode significar entrega contínua, quando o artefato fica pronto e a publicação em produção espera uma aprovação, ou deploy contínuo, quando tudo que passa nos testes vai direto para produção."
        }
      },
      {
        "@type": "Question",
        "name": "Preciso de Kubernetes para ter CI/CD?",
        "acceptedAnswer": {
          "@type": "Answer",
          "text": "Não. Uma VPS com uma pasta de releases, um symlink e um script com health check já entrega deploy automático com rollback em segundos. Kubernetes resolve outro problema, o de orquestrar muitos containers espalhados em vários servidores."
        }
      },
      {
        "@type": "Question",
        "name": "Como fazer rollback de um deploy na VPS?",
        "acceptedAnswer": {
          "@type": "Answer",
          "text": "Com releases em diretórios separados, basta apontar o symlink current para a pasta anterior e reiniciar o serviço, o que leva segundos. Com imagens Docker, o equivalente é subir de novo a tag do commit anterior. Lembre que voltar o código não desfaz migrações já aplicadas no banco."
        }
      },
      {
        "@type": "Question",
        "name": "É seguro usar runner self hosted na VPS?",
        "acceptedAnswer": {
          "@type": "Answer",
          "text": "É razoável em repositório privado, com o agente rodando sob um usuário sem root e sem acesso a dados sensíveis além do necessário. Em repositório público, o próprio GitHub desaconselha, porque pull requests de terceiros podem acabar executando código no seu servidor."
        }
      },
      {
        "@type": "Question",
        "name": "Coolify substitui um pipeline de CI/CD?",
        "acceptedAnswer": {
          "@type": "Answer",
          "text": "Para muitos projetos, sim: ele faz o build, publica em containers, emite o certificado e reimplanta a cada push. Quem já tem uma suíte de testes importante costuma manter o CI no GitHub Actions e deixar o Coolify cuidando só da publicação."
        }
      }
    ]
  }
]
```
