Deploy com rsync, SSH e Symlinks Atómicos: Como Atualizar Sites Estáticos no HestiaCP Sem Ficheiros Partidos

Deploy com rsync, SSH e Symlinks Atómicos: Como Atualizar Sites Estáticos no HestiaCP Sem Ficheiros Partidos

A Condição de Corrida nos Deploys Comuns: O Perigo de Copiar Ficheiros Diretos

Acabaste de compilar o teu site estático ultrarrápido em Astro, Hugo ou Next.js. O teu script de build gerou ficheiros HTML otimizados, folhas de estilo e ficheiros JavaScript compactados com hashes criptográficos na pasta dist/.

A seguir, disparas um comando muito comum:

# O ERRO TÍPICO: Escrever diretamente por cima da pasta de produção
rsync -avz --delete ./dist/ utilizador@servidor:/home/utilizador/web/dominio.com/public_html/

À primeira vista, este comando parece inofensivo. No entanto, num servidor em produção com tráfego real, este comando cria uma perigosa condição de corrida.

A CONDIÇÃO DE CORRIDA DURANTE UM DEPLOY COMUM

  • O rsync está a gravar 50MB de ficheiros novos (Tempo decorrido: 4 segundos…)
  • Ficheiros no disco: [ index.html (NOVO) ] [ app.b8f9.js (A GRAVAR…) ] [ style.css (ANTIGO) ]
  • Um Utilizador Entra no Site ao Segundo 2: 1. O Nginx serve o novo index.html → Que pede o ficheiro app.b8f9.js
    1. O browser pede app.b8f9.js → Mas o ficheiro ainda só está a meio!
  1. Resultado: SyntaxError: Unexpected end of input → ECRÃ BRANCO NO CLIENTE!

Durante os 4 a 8 segundos em que o rsync está a percorrer a árvore de pastas e a substituir ficheiros, a tua pasta pública fica num estado híbrido inconsistente:

  1. O ficheiro index.html já foi substituído e aponta para os novos blocos de JavaScript.
  2. O browser do teu visitante lê esse HTML e pede imediatamente o ficheiro chunk-99a2c.js.
  3. Como o rsync ainda vai a meio da lista, o Nginx responde com 404 Not Found.
  4. O utilizador fica com o site desconfigurado e a consola cheia de erros de execução.

Em produção precisas de atomicidade matemática. Ou o visitante vê a Versão A, ou vê a Versão B. Nunca pode apanhar o sistema a meio da mudança.

Nas soluções que desenvolvemos no nosso serviço de alojamento gerido e na entrega de websites estáticos rápidos, aplicamos sempre a arquitetura de symlinks atómicos para garantir zero falhas e reversões imediatas.


O padrão de excelência para deploys com paragem zero em servidores sem Docker pesado baseia-se no modelo clássico do Capistrano. Criado no ecossistema Ruby, este método tira partido das regras nativas do sistema de ficheiros em Debian GNU/Linux.

ESTRUTURA DE PASTAS PARA DEPLOY ATÓMICO

  • /home/utilizador/web/dominio.com/ releases/
  • 20261019_100000/ (Versão Antiga N-2)
  • 20261019_120000/ (Versão Anterior N-1)
  • 20261019_140000/ (Versão Mais Recente N - Já enviada a 100%)
  • shared/ uploads/ (Imagens de utilizadores e ficheiros fixos)
  • .env (Variáveis de ambiente)
  • public_html → LINK SIMBÓLICO para releases/20261019_140000/
  • O Nginx entrega a versão ativa instantaneamente!

Como Funciona Este Mecanismo:

  1. Cada envio copia os novos ficheiros para uma pasta isolada com o carimbo temporal UTC: releases/AAAAMMDD_HHMMSS/.
  2. A transferência acontece em segundo plano sem ninguém ver. Os teus visitantes continuam a navegar na versão anterior sem solavancos.
  3. Se a tua ligação cair ou o rsync falhar a meio, o site ativo não sofre rigorosamente nada. Basta apagares a pasta temporária.
  4. Quando a transferência termina com sucesso, um comando do Linux altera o apontador do link public_html para a nova diretoria.
  5. No kernel do Linux, alterar um link simbólico através de rename() é uma operação atómica. Não existe nenhum milissegundo de vazio.

Passo a Passo para Configurar no HestiaCP e Nginx

Vamos preparar o teu servidor Debian 12 com HestiaCP e Nginx para este modelo de trabalho.

1. Prepara as Diretorias no Servidor

Liga-te por SSH ao teu servidor e organiza as pastas do teu domínio:

# Entra na pasta do teu domínio no HestiaCP
cd /home/admin/web/dominio.com

# 1. Guarda a pasta antiga por precaução
mv public_html public_html_antigo

# 2. Cria as pastas de releases e armazenamento partilhado
mkdir -p releases shared/uploads

# 3. Cria uma primeira versão de teste para o Nginx não dar erro
mkdir -p releases/inicial
echo "<h1>Servidor Pronto</h1>" > releases/inicial/index.html

# 4. Cria o link simbólico inicial
ln -sfn /home/admin/web/dominio.com/releases/inicial public_html

Confirma a criação do link com ls -la:

lrwxrwxrwx 1 admin admin 44 Oct 19 14:10 public_html -> /home/admin/web/dominio.com/releases/inicial

O Script Automático de Deploy em Bash

Eis o script que podes correr no teu computador ou integrar no teu sistema de integração contínua (GitHub Actions ou GitLab):

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

# Definições do Servidor
UTILIZADOR_SSH="admin"
SERVIDOR_IP="servidor.dominio.com"
CAMINHO_REMOTO="/home/admin/web/dominio.com"
PASTA_BUILD="./dist/"
DATA_HORA=$(date -u +"%Y%m%d_%H%M%S")
PASTA_RELEASE="${CAMINHO_REMOTO}/releases/${DATA_HORA}"

echo "=== A Iniciar Deploy Atómico: ${DATA_HORA} ==="

# 1. Valida se a pasta de build existe localmente
if [ ! -d "${PASTA_BUILD}" ]; then
  echo "Erro: A pasta local de compilação ${PASTA_BUILD} não existe!"
  exit 1
fi

# 2. Cria a nova pasta de versão no servidor
ssh "${UTILIZADOR_SSH}@${SERVIDOR_IP}" "mkdir -p ${PASTA_RELEASE}"

# 3. Transfere os ficheiros com rsync
echo "A transferir ficheiros com rsync..."
rsync -avz --checksum --no-perms --no-owner --no-group \
  "${PASTA_BUILD}" "${UTILIZADOR_SSH}@${SERVIDOR_IP}:${PASTA_RELEASE}/"

# 4. Troca o symlink de forma atómica e apaga versões velhas
echo "A trocar o link simbólico e a limpar versões antigas..."
ssh "${UTILIZADOR_SSH}@${SERVIDOR_IP}" bash -c "'
  set -euo pipefail

  # Cria o symlink temporário e faz a troca imediata
  ln -sfn \"${PASTA_RELEASE}\" \"${CAMINHO_REMOTO}/public_html_tmp\"
  mv -Tf \"${CAMINHO_REMOTO}/public_html_tmp\" \"${CAMINHO_REMOTO}/public_html\"

  # Mantém apenas as 5 versões mais recentes e apaga as restantes
  cd \"${CAMINHO_REMOTO}/releases\"
  ls -dt */ | tail -n +6 | xargs rm -rf 2>/dev/null || true
'"

echo "=== Deploy Concluído com Sucesso! Versão ativa: ${DATA_HORA} ==="
  • A TROCA ATÓMICA COM mv -Tf NO KERNEL
  • Passo A: Criação do link temporário: public_html_tmp → releases/20261019_140000/ public_html → releases/20261019_120000/ (Ativo)
  • Passo B: Chamada atómica de rename (mv -Tf): public_html → releases/20261019_140000/ (Ativo!)
  • O kernel substitui o ponteiro instantaneamente sem quebras de serviço.

A combinação de criar um link temporário e movê-lo com mv -Tf é a chave de todo o processo. O parâmetro -T trata o destino como um ficheiro normal em vez de tentar mover o link para dentro da pasta, acionando a chamada de sistema rename(). Mesmo que o teu site receba centenas de pedidos por segundo nesse instante, todos os clientes recebem ficheiros válidos.


Recuperação Imediata: Como Fazer Rollback em Menos de 1 Segundo

Imagina que um erro grave de código passou despercebido nos testes e chegou a produção. Num deploy vulgar com rsync terias de esperar que o teu sistema voltasse a compilar o código anterior, demorando minutos enquanto os clientes se queixam.

Com lançamentos atómicos, reverter o site para a versão anterior demora menos de um segundo:

# Script de Reversão de Emergência (Rollback)
ssh admin@servidor.dominio.com bash -c "'
  cd /home/admin/web/dominio.com/releases
  
  # Descobre qual é a pasta da versão imediatamente anterior
  VERSAO_ANTERIOR=\$(ls -dt */ | sed -n 2p | tr -d /)
  
  if [ -z \"\$VERSAO_ANTERIOR\" ]; then
    echo \"Não foi encontrada nenhuma versão anterior para reverter!\"
    exit 1
  fi
  
  echo \"A reverter para releases/\$VERSAO_ANTERIOR...\"
  ln -sfn \"/home/admin/web/dominio.com/releases/\$VERSAO_ANTERIOR\" \"/home/admin/web/dominio.com/public_html_tmp\"
  mv -Tf \"/home/admin/web/dominio.com/public_html_tmp\" \"/home/admin/web/dominio.com/public_html\"
  echo \"Reversão concluída com sucesso em milissegundos!\"
'"

Ao guardares as últimas 5 versões no teu disco, garantes um cinto de segurança infalível. Se algo correr mal, disparas este comando e o site bom volta ao ar em 200 milissegundos.

Aliando esta estratégia aos princípios de anti-over-engineering, consegues fiabilidade e rapidez dignas de grandes infraestruturas sem precisares de ferramentas pesadas e dispendiosas para gerir os teus sites estáticos.

No terreno, em conjunto com a experiência acumulada na Webxtek Studio, aplicamos exatamente estes padrões operacionais em setores exigentes como especialistas em devops e arquitetura cloud e saas empresarial b2b. Quando alinhas a tua estrutura através de alojamento gerido para websites e aplicações e anti-over-engineering, transformas boas práticas técnicas numa vantagem competitiva real e mensurável para o teu negócio.

[+] [ INDEX_DIRECTORY ] 15 tópicos ▼
[ SYSTEM.FAQ ]

Perguntas Frequentes

Porque é que fazer rsync diretamente para a pasta public_html quebra o site durante o envio?

Quando transferes ficheiros diretamente para a pasta ativa de produção, a operação demora vários segundos. Se um visitante ou motor de busca aceder à página a meio do processo, vai receber um ficheiro HTML atualizado que tenta carregar ficheiros JavaScript ou CSS que ainda não acabaram de ser gravados no disco, provocando erros 404 e ecrãs em branco.

Como configuras a estrutura de pastas no HestiaCP mantendo a pasta public_html?

O HestiaCP espera que os ficheiros do domínio fiquem em `/home//web//public_html`. Para usares lançamentos atómicos, transformas a diretoria `public_html` num link simbólico que aponta para a pasta da versão mais recente dentro de uma pasta `releases/` (`releases/20261019_120000`).

Quantas versões antigas deves guardar no servidor e como limpas as restantes?

A recomendação ideal é manteres entre 3 e 5 versões anteriores no disco. Isto permite-te reverter um deploy com problemas em menos de 1 segundo. Podes automatizar a limpeza no final do teu script com uma linha em bash (`ls -dt releases/* | tail -n +6 | xargs rm -rf`).

> INICIAR_PROJETO

Precisa de um website que transmita confiança, apareça na pesquisa e dê mais força à sua presença digital? Comece a conversa aqui.