Tutoriais de VPS
Configurar Jenkins CI/CD em VPS (2026): Docker, Caddy HTTPS, Webhook do GitHub, agentes e backup
Implante o Jenkins LTS em uma VPS com Docker Compose e Caddy, configurando Webhooks do GitHub, agentes WebSocket, pipelines com Jenkinsfile, reforço de permissões, backup e restauração, atualizações e solução de problemas.

Depois de enviar o código para o GitHub, ainda é preciso entrar por SSH no servidor, executar manualmente git pull, instalar dependências, rodar testes e reiniciar serviços. Com poucos projetos, esse fluxo até dá para o gasto. Quando os projetos se multiplicam, o problema mais comum não é "não saber implantar", mas um passo esquecido: testes não executados, variáveis de ambiente erradas, uma versão com falha sobrescrevendo a produção, e ninguém sabe quem fez o quê.
O Jenkins resolve essa cadeia de automação. Ele consegue buscar o código após receber um push do GitHub, executar builds, testes, arquivamento de artefatos e implantação conforme o Jenkinsfile do repositório, e guardar os logs de cada execução. Mas o Jenkins também pode executar scripts de build arbitrários, então a abordagem de implantação não pode parar em "só fazer funcionar". O controller, os agentes de build, as credenciais e as permissões do Docker precisam ser separados desde o primeiro dia.
Este artigo usa como base Jenkins 2.568.2 LTS, Java 21, Docker Compose, Caddy e Ubuntu 24.04 para montar um ambiente Jenkins CI/CD adequado a equipes pequenas. Você vai concluir a implantação do controller, HTTPS, o reforço inicial, Webhooks do GitHub, pipelines, agentes WebSocket, backup e restauração, atualizações e solução de problemas comuns. As informações de versão foram conferidas na página oficial de downloads do Jenkins em setembro de 2026; no momento real da instalação, ainda vale conferir primeiro o LTS mais recente e os avisos de segurança.
O Jenkins chama de controller o serviço central. Ele guarda configuração, credenciais, jobs e registros de build, e é responsável por enfileirar e despachar tarefas para os agentes. A instalação padrão permite executar builds no nó integrado, o que é prático para testar recursos, mas não serve para uso de longo prazo.
Os scripts de build vêm do repositório, podem executar shell, baixar dependências, rodar testes e até chamar o Docker. Enquanto rodarem no controller, podem ler $JENKINS_HOME, a configuração de plugins e arquivos relacionados a credenciais. O Jenkins recomenda oficialmente definir o número de executores do controller como 0 e entregar todos os builds aos agentes.
Este artigo usa a seguinte estrutura:
GitHub Push / Pull Request
|
| HTTPS Webhook
v
Caddy :443
|
| 127.0.0.1:8080
v
Jenkins Controller
仅编排,不执行构建
^
| WebSocket over HTTPS
|
Jenkins Build Agent
拉代码、测试、构建
Os agentes WebSocket se conectam ativamente ao controller e usam apenas o 443 HTTPS existente, então não é preciso expor à internet a porta TCP de agentes do Jenkins 50000.
A vantagem do Jenkins é a grande quantidade de plugins, a liberdade de local de implantação, a capacidade de se conectar a redes internas e de gerenciar agentes em diferentes sistemas e arquiteturas. O custo é que você mesmo precisa manter o controller, os plugins, as credenciais, os backups e as atualizações.
| Opção | Vantagens | Custos principais | Mais adequada para |
|---|---|---|---|
| Jenkins | Alta liberdade de auto-hospedagem, ecossistema maduro de plugins e agentes | Exige manter controller, plugins e políticas de segurança | Vários ambientes, builds em rede interna, experiência prévia com Jenkins |
| GitHub Actions | Integração direta com o GitHub, runners hospedados prontos para uso | Dependência profunda do GitHub, auto-hospedagem complexa ainda exige operação | Projetos no GitHub e pipelines padrão |
| GitLab CI | Integrado a repositórios, permissões e artefatos do GitLab | O GitLab auto-hospedado consome bastante recurso | Equipes que já usam GitLab CE |
Se todo o código está no GitHub e o fluxo não é complexo, os runners auto-hospedados do GitHub Actions costumam economizar tempo de gestão. Se você precisa de uma plataforma de código auto-hospedada com issues, registry e CI integrados, veja o tutorial de implantação do GitLab CE e Runner. Quando já existem vários sistemas internos, builds multiplataforma ou muitos jobs do Jenkins, o Jenkins auto-hospedado se encaixa melhor.
A documentação oficial de instalação com Docker do Jenkins recomenda para equipes pequenas mais de 4 GB de RAM e mais de 50 GB de disco. É apenas o ponto de partida para o controller e uso leve; o que realmente consome recursos são compilação, testes, imagens Docker e artefatos.
| Cenário | Recomendação para o controller | Recomendação para agentes |
|---|---|---|
| Aprendizado e projetos pessoais | 2 vCPU, 4 GB de RAM, 50 GB SSD | Pode compartilhar a máquina temporariamente, mas com contêiner e usuário separados |
| Equipes pequenas de 3–10 pessoas | 2–4 vCPU, 4–8 GB de RAM, 80 GB SSD | Começar com uma máquina separada de 4 vCPU e 8 GB de RAM |
| Builds Java/Android/frontend grandes | Manter o controller estável | 8 vCPU, 16 GB ou mais, escalar conforme os picos de build |
| Multiarquitetura ou alta concorrência | Controller dedicado | Vários agentes efêmeros com labels |
Se o controller e os agentes ficarem na mesma VPS de 4 GB, a compilação pode facilmente levar a interface de administração ao OOM. Com orçamento limitado, no mínimo defina limites de memória do contêiner e configure os executores do agente como 1. Uma opção mais segura é uma VPS pequena e estável para o controller e outra máquina reconstruível a qualquer momento para os agentes.
Também não olhe só para o pacote de instalação do Jenkins ao dimensionar o disco. Os itens a seguir crescem continuamente:
- Configuração, plugins e registros de build em
$JENKINS_HOME; - Workspaces dos agentes, caches de dependências e arquivos temporários;
- Imagens Docker, caches do BuildKit e camadas de contêiner;
- Artefatos arquivados, relatórios de teste e logs de console;
- Backups locais.
Prepare um domínio dedicado, por exemplo ci.example.com, e aponte o registro A para a VPS do controller. Não adicione registros AAAA se o IPv6 não estiver configurado corretamente.
O controller só precisa de:
| Porta | Uso | Recomendação |
|---|---|---|
22/TCP | Administração SSH | Restringir a IPs de administração, usar login por chave |
80/TCP | Validação de certificado e redirecionamentos do Caddy | Abrir para a internet |
443/TCP | Web do Jenkins, Webhooks, agentes WebSocket | Abrir para as origens necessárias |
8080/TCP | HTTP do contêiner Jenkins | Vincular apenas a 127.0.0.1 |
50000/TCP | Agentes inbound tradicionais | Não usado neste artigo, não abrir |
O GitHub.com precisa entregar Webhooks ao Jenkins, então 443 deve ser acessível pela internet. Se o Jenkins atender apenas uma rede interna, você pode dispensar os Webhooks do GitHub e usar um túnel controlado, um GitHub App ou SCM Poll agendado; não exponha todo o painel de administração sem proteção só por causa de um callback.
Se aparecer "o contêiner está bem, mas o domínio não abre", siga o guia de solução de problemas de portas, firewall, grupos de segurança e endereços de escuta para verificar das portas locais para fora, em vez de desativar o UFW de imediato.
O exemplo a seguir usa Ubuntu 24.04. Recomenda-se instalar o Docker pelo repositório oficial; se você já tem um ambiente Docker, não execute scripts de um clique de origem desconhecida.
sudo apt update
sudo apt full-upgrade -y
sudo apt install -y ca-certificates curl caddy openssl jq
docker --version
docker compose version
sudo systemctl enable --now caddy
Ambientes de produção também precisam de rotação de logs, limites de recursos, verificações de integridade e rollback; confira com a lista de configuração de produção do Docker Compose.
sudo install -d -m 750 -o "$USER" -g "$USER" /opt/jenkins
cd /opt/jenkins
Este artigo usa um volume nomeado do Docker para guardar $JENKINS_HOME. O UID do usuário de serviço na imagem oficial do Jenkins costuma ser 1000; volumes nomeados reduzem os problemas de permissão comuns em bind mounts do host.
Crie /opt/jenkins/compose.yaml:
services:
jenkins:
image: jenkins/jenkins:2.568.2-lts-jdk21
container_name: jenkins-controller
restart: unless-stopped
environment:
JAVA_OPTS: >-
-Djava.awt.headless=true
-Djenkins.install.runSetupWizard=true
JENKINS_OPTS: >-
--httpPort=8080
ports:
- "127.0.0.1:8080:8080"
volumes:
- jenkins_home:/var/jenkins_home
mem_limit: 3g
cpus: 2.0
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:8080/login >/dev/null || exit 1"]
interval: 30s
timeout: 10s
retries: 10
start_period: 90s
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
volumes:
jenkins_home:
name: jenkins_home
Aqui 50000 deliberadamente não é mapeado, e /var/run/docker.sock não é montado no controller. Entregar o socket do Docker ao controller equivale a dar privilégios de host a quem puder controlar builds; mais adiante vamos mover os builds para um agente separado.
mem_limit: 3g é um ponto de partida para uma VPS com mais de 4 GB que executa apenas o controller. Se a mesma máquina também tiver bancos de dados ou outros aplicativos, redistribua a memória conforme o uso real; não copie os números e deixe o sistema usar swap por muito tempo.
Primeiro verifique a configuração final:
cd /opt/jenkins
docker compose config --quiet
docker compose pull
docker compose up -d
docker compose ps
A primeira inicialização precisa descompactar o Jenkins, inicializar diretórios e carregar metadados de plugins, então a verificação de integridade pode levar um ou dois minutos. Veja os logs:
docker compose logs -f --tail=200 jenkins
Quando aparecer a mensagem de que o Jenkins terminou de iniciar, pressione Ctrl+C para sair dos logs. Confirme que a porta está vinculada apenas ao endereço de loopback:
curl -fsSI http://127.0.0.1:8080/login
ss -lnt | grep ':8080'
O endereço de escuta esperado é 127.0.0.1:8080, não 0.0.0.0:8080.
Crie ou edite /etc/caddy/Caddyfile:
ci.example.com {
encode zstd gzip
reverse_proxy 127.0.0.1:8080
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
}
}
O Caddy passa o Host por padrão e define X-Forwarded-For, X-Forwarded-Proto e X-Forwarded-Host. Essa é exatamente a informação que o Jenkins precisa para gerar redirecionamentos HTTPS e endereços de callback corretos.
Verifique e recarregue:
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
sudo systemctl status caddy --no-pager
curl -fsSI https://ci.example.com/login
Colocar o Jenkins na raiz de um subdomínio dedicado é o mais simples. Se você realmente precisar usar https://example.com/jenkins/, o controller e o proxy reverso devem usar exatamente o mesmo context path, e você deve adicionar --prefix=/jenkins para o Jenkins. Alterar apenas o caminho no Caddy sem alterar o Jenkins costuma resultar em 404 em recursos estáticos, redirecionamentos para URLs erradas ou avisos do backend de proxy reverso quebrado. Para mais configuração de proxy reverso, veja o tutorial de HTTPS automático com Caddy.
Ao acessar https://ci.example.com/ pela primeira vez, você verá a página Unlock Jenkins. Leia a senha de uso único:
cd /opt/jenkins
docker compose exec jenkins \
cat /var/jenkins_home/secrets/initialAdminPassword
Após colar a senha, instale os plugins sugeridos e crie um novo administrador pessoal. Não use a longo prazo um admin compartilhado nem guarde a senha inicial no chat da equipe.
Após concluir o assistente inicial, vá em Manage Jenkins → System e defina Jenkins URL como:
https://ci.example.com/
Esse endereço é usado para Webhooks, e-mail e links de página. Ele deve coincidir com o endereço HTTPS que os usuários realmente acessam, senão o backend pode avisar It appears that your reverse proxy setup is broken.
Vá em:
Manage Jenkins → Nodes → Built-In Node → Configure
Defina Number of executors como 0 e salve. Jobs sem agente ficarão na fila; isso é normal. Termine a configuração do agente antes de começar builds reais.
Vá em Manage Jenkins → Security:
- Mantenha a autenticação ativada; não permita que usuários anônimos tenham permissões de administração ou configuração de jobs;
- Mesmo em equipes pequenas, não use
Logged-in users can do anythingcomo estratégia de autorização de longo prazo; - Ao instalar e configurar o Matrix Authorization Strategy, comece pelo menor privilégio;
- Apenas alguns administradores devem ter
Overall/Administer; - Restrinja
Credentials/Createe as permissões de configuração de jobs; - Mantenha o Agent → Controller Access Control ativado; não o desative só para silenciar erros de plugins antigos.
Se o painel de administração precisar ser acessível publicamente, adicione VPN, proxy de identidade ou restrição por IP fixo na frente do Caddy. Quando Webhooks e a interface de administração compartilham o domínio, teste as regras de restrição separadamente para não bloquear também /github-webhook/.
Quanto mais plugins, mais complexas ficam as dependências e as atualizações de segurança. Para começar, normalmente você só precisa de:
- Pipeline;
- Git;
- GitHub ou GitHub Branch Source;
- Credentials Binding;
- Matrix Authorization Strategy;
- Docker Pipeline, se for usá-lo nos agentes.
Após instalar, vá em Manage Jenkins → Plugins para verificar atualizações e avisos de segurança. Não instale plugins sem manutenção há anos só para copiar um tutorial antigo.
Os agentes podem ficar em outra VPS ou, temporariamente, em um contêiner separado no mesmo host do controller. A abordagem de máquina única não oferece alta disponibilidade, mas ao menos impede que os processos de build leiam diretamente jenkins_home.
No Jenkins, vá em:
Manage Jenkins → Nodes → New Node
Crie um Permanent Agent, por exemplo:
- Node name:
linux-agent-01 - Number of executors:
1 - Remote root directory:
/home/jenkins/agent - Labels:
linux docker-small - Usage: Only build jobs with label expressions matching this node
- Launch method: Launch agent by connecting it to the controller
- WebSocket: ativado
Após salvar, o Jenkins mostra o comando de conexão e o Agent Secret. O secret equivale à credencial de login do agente; não o escreva no Git.
Crie .agent.env em /opt/jenkins:
cd /opt/jenkins
umask 077
cat > .agent.env <<'EOF'
JENKINS_URL=https://ci.example.com/
JENKINS_AGENT_NAME=linux-agent-01
JENKINS_AGENT_SECRET=把这里替换为节点页面显示的真实Secret
EOF
chmod 600 .agent.env
Adicione o agente em services dentro de compose.yaml. Aqui se usa a imagem oficial de agente inbound com JDK 21:
agent:
image: jenkins/inbound-agent:jdk21
container_name: jenkins-agent-01
restart: unless-stopped
env_file:
- .agent.env
command:
- -url
- ${JENKINS_URL}
- -secret
- ${JENKINS_AGENT_SECRET}
- -name
- ${JENKINS_AGENT_NAME}
- -webSocket
- -workDir
- /home/jenkins/agent
volumes:
- jenkins_agent_work:/home/jenkins/agent
mem_limit: 3g
cpus: 2.0
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
Também adicione o seguinte no final do arquivo em volumes:
jenkins_agent_work:
name: jenkins_agent_work
Há um problema fácil de passar despercebido: o Compose por padrão só lê .env para expansão de ${变量}, enquanto env_file apenas coloca variáveis no contêiner. O mais simples é alterar command para URLs e nomes de nó fixos e não sensíveis, e expandir apenas o secret a partir de .env; ou especificá-lo explicitamente na inicialização:
cd /opt/jenkins
docker compose --env-file .agent.env config --quiet
docker compose --env-file .agent.env up -d agent
docker compose logs --tail=100 agent
Confirme na página do nó do Jenkins que o status é Connected. As conexões WebSocket passam por https://ci.example.com/ e não precisam de 50000.
jenkins/inbound-agent:jdk21 fornece principalmente o ambiente de execução do agente e não inclui automaticamente Node.js, Maven, Go, Android SDK ou Docker CLI. Crie sua própria imagem de agente com versões fixas para as ferramentas que o projeto precisar. Por exemplo:
FROM jenkins/inbound-agent:jdk21
USER root
RUN apt-get update \
&& apt-get install -y --no-install-recommends git curl make \
&& rm -rf /var/lib/apt/lists/*
USER jenkins
Não deixe os agentes rodarem como root permanentemente. Toda alteração de imagem deve passar por revisão de código, e registre tags ou digests das imagens base.
Muitos tutoriais adicionam esta linha aos agentes:
- /var/run/docker.sock:/var/run/docker.sock
Isso realmente permite executar docker build imediatamente, mas um build com acesso ao socket do Docker equivale basicamente a ter privilégios de root no host. Qualquer pessoa que possa alterar o Jenkinsfile pode montar o diretório raiz do host, ler segredos de outros contêineres ou controlar o controller.
Opções mais seguras incluem:
- Usar uma VPS de agente dedicada e reconstruível que não execute mais nada;
- Usar máquinas virtuais de curta duração ou agentes de pod do Kubernetes;
- Usar um BuildKit/Docker Builder remoto protegido por TLS;
- Usar agentes totalmente isolados e sem credenciais de produção para pull requests não confiáveis;
- Separar a implantação de produção e os testes comuns em agentes, labels e escopos de credenciais diferentes.
Se for apenas um projeto pessoal e você aceita o risco do socket do Docker na mesma máquina, deixe claro mesmo assim que esse agente é uma máquina de build descartável que não executa bancos de dados, gerenciadores de senhas ou outros serviços críticos.
As credenciais do Jenkins não devem ser escritas em scripts de pipeline nem em parâmetros de jobs. Vá em:
Manage Jenkins → Credentials
Escolha conforme a finalidade:
- Buscar repositórios privados: deploy key ou GitHub App com privilégio mínimo;
- Chamar a API do GitHub: GitHub App ou PAT fine-grained com privilégio mínimo;
- Servidores de implantação: chave SSH dedicada;
- Registry: conta de bot dedicada ou token de curta duração.
Limite as credenciais a pastas específicas sempre que possível em vez de tornar tudo global. O Jenkins recomenda oficialmente começar por "sem permissões por padrão" e liberar apenas para os projetos e pessoas que realmente precisam.
Os pipelines referenciam segredos pelo ID da credencial e nunca leem o valor real. Por exemplo:
withCredentials([string(credentialsId: 'registry-token', variable: 'TOKEN')]) {
sh '''
set +x
./scripts/publish.sh "$TOKEN"
'''
}
Usar aspas simples triplas no Groovy permite que o shell expanda variáveis em tempo de execução, reduzindo o risco de segredos aparecerem em argumentos de processo ou logs após a interpolação do Groovy. Mesmo que o Jenkins tente mascará-los, nunca coloque pipelines não confiáveis e credenciais de produção no mesmo escopo de permissões.
Após instalar o plugin do GitHub, a URL comum do Webhook é:
https://ci.example.com/github-webhook/
Não esqueça a barra final. Vá ao repositório do GitHub:
Settings → Webhooks → Add webhook
Preencha:
- Payload URL:
https://ci.example.com/github-webhook/ - Content type:
application/json - Secret: gere um valor aleatório independente e use o mesmo valor na configuração correspondente de GitHub Server/Webhook no Jenkins
- Events: selecione apenas os eventos Push, Pull request e outros que o job precisar
- Active: ativado
Gere o secret:
openssl rand -hex 32
Não reutilize a senha de administrador, o PAT do GitHub nem o secret do agente. O secret do Webhook serve para verificar que as requisições vêm de quem possui esse secret e não deve conceder permissões da API do GitHub.
No job, ative GitHub hook trigger for GITScm polling; em Multibranch Pipeline, configure o GitHub Branch Source para que o plugin escaneie branches e pull requests.
Na página do Webhook no GitHub, clique em Recent Deliveries para verificar:
- O código de status HTTP;
- O tipo de evento e o Delivery ID em Request Headers;
- O conteúdo de Response;
- Se o job correspondente do Jenkins foi disparado.
Receber HTTP 200 significa apenas que o endpoint aceitou a requisição, não que o pipeline tenha rodado. Verifique também a URL do repositório, as regras de branch, o trigger do job e os logs do sistema do Jenkins.
Recomenda-se escrever o pipeline em Jenkinsfile na raiz do repositório em vez de guardá-lo apenas na interface web do Jenkins. Assim o processo de build acompanha a revisão de código e o histórico de versões.
O exemplo a seguir é mínimo e não toca diretamente em servidores de produção:
pipeline {
agent { label 'linux' }
options {
timestamps()
disableConcurrentBuilds()
buildDiscarder(logRotator(numToKeepStr: '30'))
timeout(time: 20, unit: 'MINUTES')
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Environment') {
steps {
sh '''
set -eu
uname -a
git --version
'''
}
}
stage('Test') {
steps {
sh '''
set -eu
./scripts/ci-test.sh
'''
}
}
stage('Package') {
steps {
sh '''
set -eu
./scripts/ci-package.sh
'''
}
}
}
post {
always {
junit allowEmptyResults: true, testResults: 'reports/**/*.xml'
archiveArtifacts allowEmptyArchive: true, artifacts: 'dist/**'
}
cleanup {
deleteDir()
}
}
}
Coloque os comandos específicos de linguagem e framework em scripts/ci-test.sh e scripts/ci-package.sh do repositório, para que rodem tanto localmente quanto no Jenkins; solucionar problemas é mais fácil do que enfiar dezenas de linhas de shell no Groovy.
Crie um Pipeline ou Multibranch Pipeline, escolha Pipeline script from SCM e preencha o repositório e o caminho de Jenkinsfile. Na primeira execução, confirme que o job aparece em linux-agent-01, não no Built-In Node.
Depois que testes e empacotamento passam, muita gente faz diretamente:
ssh root@production "cd /app && git pull && docker compose up -d"
Esse comando é simples, mas entrega a chave privada de root ao ambiente de build, e o rollback e a auditoria ficam fracos. Um padrão mínimo melhor é:
- Criar um usuário de sistema dedicado à implantação, sem login por senha;
- Guardar a chave SSH apenas em uma credencial do Jenkins de uma pasta específica;
- Restringir os comandos e diretórios que o usuário de implantação pode acessar;
- Primeiro gerar artefatos com números de versão ou digests imutáveis;
- Implantar uma versão explícita, não o
git pullem produção; - Parar e reverter automaticamente se a verificação de integridade falhar;
- Exigir aprovação para implantações de produção ou permitir apenas branches protegidas.
Não deixe pull requests de colaboradores externos tocarem em credenciais de implantação. Agentes de teste de pull request e agentes de implantação de produção devem usar labels, redes e escopos de credenciais diferentes.
Os jobs do Jenkins podem manter um número limitado de registros de build via buildDiscarder, mas os caches do Docker e os workspaces dos agentes continuam crescendo.
Verifique semanalmente:
df -h
docker system df
docker volume ls
docker compose logs --tail=100 jenkins
Não execute docker system prune -a --volumes sem confirmar para que serve. Ele pode apagar imagens, caches e volumes não montados que outros projetos ainda precisam. Recomendações:
- Definir retenção de builds por job;
- Enviar artefatos grandes para armazenamento de objetos ou repositório de artefatos;
- Usar discos dedicados para agentes;
- Definir uma política controlada de limpeza de cache para o Docker BuildKit;
- Alertar separadamente sobre
$JENKINS_HOMEe os workspaces dos agentes; - Não acumular backups locais no mesmo disco do sistema por muito tempo.
A configuração, os plugins, os jobs e os registros de build do Jenkins ficam principalmente em $JENKINS_HOME, que neste artigo corresponde ao volume nomeado jenkins_home. Quando os requisitos de consistência são altos, o oficial recomenda snapshots do sistema de arquivos ou do armazenamento. Equipes pequenas também podem parar o controller em uma janela de manutenção e criar um arquivo.
Crie o diretório:
sudo install -d -m 700 -o "$USER" -g "$USER" /opt/jenkins/backups
sudo install -d -m 700 -o "$USER" -g "$USER" /opt/jenkins/controller-key
Primeiro entre em Quiet Down e espere os builds atuais terminarem:
Manage Jenkins → Prepare for Shutdown
Pare o controller e arquive o volume:
cd /opt/jenkins
docker compose stop jenkins
docker run --rm \
-v jenkins_home:/data:ro \
-v /opt/jenkins/backups:/backup \
alpine:3.22 \
tar --exclude='./workspace' \
--exclude='./caches' \
--exclude='./secrets/master.key' \
-czf "/backup/jenkins-home-$(date +%F-%H%M%S).tar.gz" \
-C /data .
docker compose start jenkins
docker compose ps
O Jenkins alerta especialmente que master.key não deve ser guardado junto com os backups comuns. Quem tiver o backup comum e a chave do controller pode descriptografar as credenciais guardadas pelo Jenkins. Copie a chave separadamente para outro armazenamento criptografado estritamente controlado:
docker run --rm \
-v jenkins_home:/data:ro \
-v /opt/jenkins/controller-key:/key \
alpine:3.22 \
sh -c 'umask 077; cp /data/secrets/master.key /key/master.key'
chmod 600 /opt/jenkins/controller-key/master.key
Após copiar para um cofre de senhas offline ou armazenamento criptografado separado, não envie a chave do controller e os arquivos comuns para o mesmo bucket nem para o mesmo escopo de credenciais. Os backups comuns também contêm configuração sensível e precisam ser criptografados.
Além do volume, também é preciso fazer backup de:
/opt/jenkins/compose.yaml;- Caddyfile;
- Dockerfiles de agentes personalizados;
- Lista de plugins e versão da imagem do Jenkins;
- Cópia criptografada de
.agent.envou o processo para registrar agentes novamente; - Passos de restauração e resultado do último simulado.
Após o backup, sincronize para outra máquina ou armazenamento de objetos. Para restic, rclone, políticas de retenção e simulados de restauração específicos, veja o guia de simulado de backup e restauração em VPS.
Não espere o disco do controller falhar para descompactar um backup pela primeira vez. Faça simulados pelo menos a cada trimestre em um ambiente isolado:
- Criar um volume vazio;
- Descompactar
jenkins-home-*.tar.gz; - Restaurar
secrets/master.keyde um local seguro separado; - Iniciar com a mesma imagem do Jenkins e versões de plugins do backup;
- Fazer login e verificar jobs, pastas, metadados de credenciais e nós;
- Executar um pipeline sem permissões de produção contra um repositório de teste;
- Verificar Webhooks, agentes e arquivamento de artefatos;
- Registrar o objetivo de tempo de recuperação e os arquivos faltantes.
Não aproveite a restauração para atualizar o Jenkins e todos os plugins. O objetivo da recuperação de desastres é restaurar primeiro o estado original; a atualização de versões deve ser outra mudança com backup e testes.
O Jenkins LTS atualiza sua linha de base e correções periodicamente. Antes de atualizar:
- Ler o changelog do LTS, o guia de atualização e os avisos de segurança;
- Fazer backup de
$JENKINS_HOMEe da chave do controller separadamente; - Exportar os plugins instalados e suas versões;
- Testar a inicialização do controller e os pipelines principais em uma cópia do backup;
- Verificar a versão do Java e as versões mínimas de Jenkins dos plugins;
- Manter a imagem antiga e os passos completos de rollback.
Liste os plugins:
cd /opt/jenkins
docker compose exec jenkins \
jenkins-plugin-cli --list
Após confirmar a nova versão, altere compose.yaml para uma tag LTS explícita; não use um latest não rastreável na configuração de produção. Depois:
cd /opt/jenkins
docker compose pull jenkins
docker compose up -d jenkins
docker compose logs -f --tail=200 jenkins
Limpe as imagens antigas só depois de verificar login, reconexão de agentes, Webhooks e pipelines de teste. As atualizações de plugins também devem ser feitas em lotes; atualizar dezenas de plugins de uma vez dificulta identificar qual dependência causou a falha.
cd /opt/jenkins
docker compose ps
docker compose logs --tail=200 jenkins
curl -fsSI http://127.0.0.1:8080/login
ss -lnt | grep ':8080'
Se o 127.0.0.1:8080 local também não funcionar, resolva primeiro o contêiner do Jenkins, a memória ou as permissões do volume; se local funcionar, verifique Caddy, DNS e firewall.
Verifique se Manage Jenkins → System → Jenkins URL é o endereço HTTPS externo e confirme que o Caddy passa corretamente o Host e X-Forwarded-*. Se usar um subcaminho, o --prefix do controller e o caminho do Caddy devem coincidir.
Verifique em ordem:
- O tipo de evento em GitHub Recent Deliveries;
- Se a Payload URL termina em
/github-webhook/; - Se o job tem o GitHub Hook Trigger ativado;
- Se a URL do repositório do job coincide com o repositório do payload;
- As regras de descoberta de branches do Multibranch;
- O plugin do GitHub e os logs do sistema;
- Se a fila está esperando porque não há agente correspondente.
Veja os logs do agente:
cd /opt/jenkins
docker compose --env-file .agent.env logs --tail=200 agent
curl -fsSI https://ci.example.com/login
Causas comuns incluem sufixo de caminho errado na URL, nome ou secret do agente atualizados, Caddy sem suporte a conexões WebSocket normais, cadeia de certificados não confiada pelo agente e hora do sistema incorreta. Ao recriar um nó, o secret antigo deixa de valer, então é preciso atualizar .agent.env.
Confirme que o agente está Connected, que as labels coincidem com o agent { label 'linux' } do Jenkinsfile e que os executores do agente não são 0. Manter os executores do controller em 0 é a configuração correta; não volte a rodar builds no controller só para a fila sumir.
A imagem de agente inbound não é um ambiente de build completo. Entre no agente e verifique:
docker compose --env-file .agent.env exec agent sh
java -version
git --version
As ferramentas que faltarem devem ser adicionadas a um Dockerfile de agente versionado e a imagem reconstruída; não instale manualmente no contêiner em execução toda vez, porque as alterações desaparecem após a reconstrução.
docker run --rm -v jenkins_home:/data alpine:3.22 \
sh -c 'ls -ldn /data; find /data -maxdepth 1 -printf "%u:%g %p\n" | head'
A imagem oficial costuma rodar com UID 1000. Não execute diretamente chmod -R 777, pois isso tornaria credenciais e configuração legíveis por outros processos. Primeiro descubra se um contêiner de backup rodando como root, um bind mount errado ou uma cópia manual mudou o proprietário, e depois corrija com o UID/GID exato.
df -h
docker system df
docker run --rm -v jenkins_home:/data:ro alpine:3.22 \
du -h -d 2 /data | sort -h | tail -30
Foque nos registros de build dos jobs, artefatos arquivados, workspaces, caches de plugins e camadas Docker dos agentes. Confirme os requisitos de retenção antes de apagar e não esvazie o volume jenkins_home como se fosse cache.
- O Jenkins usa tags explícitas de LTS e JDK 21;
-
8080está vinculado apenas a127.0.0.1; -
50000não está exposto sem propósito; - Caddy HTTPS, Jenkins URL e domínio externo coincidem;
- A senha inicial de administrador já foi substituída;
- Os executores do nó integrado são
0; - Os builds rodam apenas em agentes com labels explícitas;
- O controller não monta o socket do Docker;
- Os secrets dos agentes e as permissões de
.agent.envestão controlados; - Usuários anônimos e usuários comuns não têm permissões excessivas;
- O número de plugins está controlado e os avisos de segurança foram tratados;
- Os Webhooks do GitHub assinam apenas os eventos necessários;
- As credenciais do GitHub, registry e implantação usam privilégio mínimo;
- Pull requests não confiáveis não conseguem acessar credenciais de produção;
- Jobs, artefatos e caches do Docker têm políticas de retenção;
-
$JENKINS_HOMEtem backup criptografado em outra máquina; -
master.keyé guardado separado dos backups comuns; - O processo de restauração já foi testado em ambiente isolado;
- Antes de atualizar, são verificados o guia de atualização LTS e a compatibilidade dos plugins.
O Jenkins recomenda oficialmente mais de 4 GB de RAM e mais de 50 GB de disco para equipes pequenas. Quando o controller só orquestra, 2–4 vCPU e 4 GB de RAM podem servir de ponto de partida; compilação, testes e builds Docker devem rodar em agentes separados. Se o controller e os agentes compartilharem a máquina, recomenda-se pelo menos 8 GB e limitar a concorrência dos agentes.
Não. Agentes inbound podem se conectar via WebSocket pelo endereço HTTPS existente do Jenkins, sem precisar habilitar uma porta TCP de agente adicional. Este artigo usa esse método. Só quando você usa explicitamente o protocolo TCP de agente tradicional é preciso avaliar uma porta fixa e regras de firewall.
Tecnicamente sim, mas é muito arriscado. Um processo que controla o socket do Docker normalmente consegue obter capacidades de root no host, e o controller também guarda configuração e credenciais. No mínimo, mova os builds Docker para agentes separados; em produção, prefira agentes efêmeros, VPS de build dedicadas ou builders remotos.
O endereço comum do plugin do GitHub é https://你的Jenkins域名/github-webhook/; atenção à barra final. Depois que o GitHub retorna 200, ainda é preciso confirmar o tipo de evento, a correspondência de repositório, o trigger do job, as regras de branch e a fila de agentes; não confie apenas no status HTTP.
Não totalmente. Os backups comuns de $JENKINS_HOME precisam ser criptografados e guardados em outra máquina, e secrets/master.key deve ser guardado separadamente em um domínio de segurança diferente, conforme a recomendação oficial. Também é preciso manter o Compose, o Caddyfile, as imagens de agentes personalizadas, as versões de plugins e os passos de restauração, e realizar simulados de restauração de verdade.
Quando todo o código está no GitHub e os pipelines são bem padronizados, o GitHub Actions costuma exigir menos manutenção. Se você já auto-hospeda o GitLab, prefira o GitLab CI. Quando precisa de acesso à rede interna, agentes multissistema, plugins complexos ou migrar jobs existentes, a flexibilidade do Jenkins vale mais, mas é preciso aceitar o custo operacional contínuo de plugins, segurança e backups.
- Downloads do Jenkins e versões LTS
- Instalação oficial do Jenkins com Docker
- Isolamento do controller do Jenkins
- Guia de uso de agentes do Jenkins
- Portas de rede do Jenkins e agentes WebSocket
- Plugin do GitHub e Webhooks do Jenkins
- Segurança de credenciais no Jenkins
- Backup e restauração do Jenkins
- Solução de problemas de proxy reverso no Jenkins
O verdadeiro critério de entrada em produção do Jenkins não é a página inicial abrir, mas sim o controller não executar builds, as permissões dos agentes serem controláveis, os Webhooks dispararem com precisão, as credenciais não irem parar nos logs, os backups poderem ser restaurados e uma atualização com falha conseguir voltar ao estado original. Primeiro teste commits, testes, artefatos e notificações de falha com um repositório de teste sem permissões de produção, e depois conecte a implantação real; é muito mais seguro do que entregar chaves root ao Jenkins logo de cara.