Fazendo deploy do Rebase na Hetzner Cloud
A Hetzner Cloud oferece uma relação custo-benefício excepcionalmente boa e é uma excelente escolha para projetos que precisam de soberania de dados europeia, com datacenters em Nuremberg, Falkenstein e Helsinki.
Nada aqui sobre o seu projeto é específico da Hetzner. Um deploy do Rebase consiste em duas partes separáveis — a imagem de runtime publicada e o bundle que o rebase build produz — e o mesmo bundle roda sob o Docker Compose em um laptop, no Rebase Cloud, no Helm chart e em uma máquina da Hetzner. Mudar entre eles é uma mudança de infraestrutura, não da aplicação.
O caminho mais rápido: Terraform
Seção intitulada “O caminho mais rápido: Terraform”O módulo terraform-hcloud-rebase provisiona o servidor, um firewall, um IP estável e — a parte que importa — um volume que armazena os dados do Postgres, para que a substituição do host não destrua o banco de dados.
module "rebase" { source = "rebasepro/rebase/hcloud"
domain = "api.example.com" cors_origins = ["https://app.example.com"] ssh_public_keys = [file(pathexpand("~/.ssh/id_ed25519.pub"))]
bundle_url = "https://storage.example.com/bundles/app-1.4.0.tar.gz"
s3_bucket = "example-uploads" s3_access_key_id = var.s3_access_key_id s3_secret_access_key = var.s3_secret_access_key}Uma coisa importante antes do primeiro apply: o registro A para domain já deve apontar para o servidor, caso contrário o desafio Let’s Encrypt do Caddy falhará. O endereço é criado independentemente do servidor, portanto você pode obtê-lo primeiro com terraform apply -target=hcloud_primary_ip.ipv4, configurar o DNS e então aplicar adequadamente.
O restante desta página aborda o mesmo deploy manualmente.
1. Provisionar um servidor
Seção intitulada “1. Provisionar um servidor”- No Hetzner Cloud Console, clique em Add Server.
- Escolha uma Location — Falkenstein, Nuremberg ou Helsinki para residência de dados na UE.
- Escolha uma Image: Ubuntu 24.04.
- Escolha um Type:
CPX21(3 vCPUs / 4GB) é um patamar mínimo viável,CX32(4 vCPUs / 8GB) é confortável para o runtime mais o Postgres. - Adicione um Volume para o banco de dados. Os dados no próprio disco do servidor são perdidos junto com o servidor.
- Adicione sua chave SSH e crie-o.
2. Instalar o Docker
Seção intitulada “2. Instalar o Docker”ssh root@<your-server-ip>apt update && apt install -y docker.io docker-compose-v23. Enviar o bundle para o servidor
Seção intitulada “3. Enviar o bundle para o servidor”Não há imagem de aplicação para construir. O rebase build produz um diretório dist-bundle, e a imagem de runtime publicada o executa:
rebase buildrsync -a dist-bundle/ root@<your-server-ip>:/opt/rebase/dist-bundle/Para um deploy real, prefira uma das duas abordagens que não envolvam copiar arquivos manualmente para o servidor:
- Embutir em uma imagem —
FROM rebasepro/server:0.24.0e depoisCOPY dist-bundle /bundle, e faça o deploy alterando uma tag. - Servir via HTTP — defina
REBASE_BUNDLE_URLe o runtime baixa e descompacta o bundle a cada inicialização. Isso é o que o módulo Terraform acima faz e é o mesmo mecanismo usado pelo Helm chart.
4. Configurar e executar
Seção intitulada “4. Configurar e executar”O Rebase fornece um arquivo Compose exatamente para isso: infra/docker/docker-compose.selfhost.yml. É a receita canônica de self-hosting — Postgres e o runtime, com seu bundle montado — e vale a pena lê-lo em vez de apenas copiar, porque seus comentários explicam cada escolha.
Crie o ambiente que ele espera:
POSTGRES_PASSWORD=a_long_random_stringJWT_SECRET=another_long_random_string_at_least_32_charsREBASE_SERVICE_KEY=a_third_long_random_string_at_least_32_charsCORS_ORIGINS=https://app.yourdomain.comREBASE_ADMIN_EMAIL=you@yourdomain.comREBASE_ADMIN_PASSWORD=at_least_twelve_charactersREBASE_ADMIN_EMAIL e REBASE_ADMIN_PASSWORD são novos: na versão 0.17.3,
a primeira conta a se registrar torna-se a administradora, inclusive em produção.
Todas as seis são obrigatórias — o arquivo Compose as declara com ${VAR:?…} e
se recusa a fazer a interpolação sem elas.
As duas últimas correspondem ao primeiro administrador. Um banco de dados recém-criado não tem usuários e, fora de produção, o primeiro cadastro é promovido a admin — o que se torna uma condição de corrida no momento em que esta máquina responde em um hostname, pois o Caddy ativa o TLS antes mesmo que você tenha digitado qualquer coisa. Portanto, em produção essa janela é fechada e a conta é especificada aqui; o runtime a cria uma única vez, enquanto a tabela de usuários estiver vazia, e não faz nada nas inicializações seguintes. Faça login e altere a senha.
Em seguida, inicie:
docker compose -f infra/docker/docker-compose.selfhost.yml --env-file .env up -dO runtime escuta na porta 8080 dentro da rede do Compose.
REBASE_SERVICE_KEY ignora a segurança em nível de linha (row-level security). Trate-a como uma credencial de superusuário do banco de dados, e não como uma chave de API.
5. Terminar TLS com o Caddy
Seção intitulada “5. Terminar TLS com o Caddy”Nunca exponha o runtime diretamente. O Caddy provisiona certificados Let’s Encrypt automaticamente; executá-lo como outro serviço do Compose mantém toda a stack em um único arquivo:
caddy: image: caddy:2-alpine restart: unless-stopped ports: ["80:80", "443:443", "443:443/udp"] volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - caddy-data:/dataCom um Caddyfile assim:
api.yourdomain.com { reverse_proxy api:8080}Aponte o registro A desse domínio para o servidor antes de iniciar o Caddy, caso contrário a solicitação do certificado falhará.
Armazenamento não é opcional
Seção intitulada “Armazenamento não é opcional”O runtime se recusa a iniciar em produção com armazenamento local configurado, pois o sistema de arquivos do contêiner é destruído a cada reinicialização e um backend local em produção significa perda silenciosa de dados.
O Hetzner Object Storage é compatível com S3 e fica nos mesmos datacenters, sendo a combinação natural:
STORAGE_TYPE=s3S3_BUCKET=my-uploadsS3_ENDPOINT=https://fsn1.your-objectstorage.comS3_REGION=fsn1S3_ACCESS_KEY_ID=...S3_SECRET_ACCESS_KEY=...Se o seu projeto não armazena uploads, defina FORCE_LOCAL_STORAGE=true para confirmar isso explicitamente. Consulte Storage para obter o panorama completo.
O que a inicialização faz com seu schema
Seção intitulada “O que a inicialização faz com seu schema”Com o REBASE_MIGRATE_ON_BOOT em seu valor padrão de ensure, o runtime provisiona as tabelas das suas coleções e suas políticas de row-level security na inicialização, de forma aditiva. Uma primeira inicialização contra um banco de dados vazio já sobe disponibilizando-os — não há nenhuma etapa de schema para executar antes que o deploy funcione.
O que a inicialização deliberadamente nunca faz é algo destrutivo: ela não altera o tipo de uma coluna, não remove uma coluna nem edita um rótulo de enum existente. O reinício de um contêiner não deve ser capaz de remodelar um schema como efeito colateral.
Uma coisa, portanto, ainda precisa do rebase db push, executado a partir de um checkout ou CI onde a validação de mudanças destrutivas e um backup estejam ao alcance: qualquer alteração que não seja puramente aditiva — uma coluna renomeada, um tipo restringido, um campo removido.
Se o módulo ou o arquivo Compose vincularam o Postgres ao loopback — ambos fazem isso —, acesse-o por meio de um túnel SSH:
ssh -N -L 5433:127.0.0.1:5432 root@<your-server-ip>Uma porta de banco de dados aberta para a internet é como um deploy do Rebase acaba tendo suas linhas lidas burlando o row-level security em vez de passar por ele.
Atualizando
Seção intitulada “Atualizando”Altere a tag da imagem e reinicie. Seu bundle permanece intacto, e todos os projetos nesse runtime passam a usar o novo engine.
A exceção é a versão major do Postgres: o Postgres se recusa a iniciar com um diretório de dados gravado por uma versão major mais antiga, portanto essa atualização exige dump e restore, nunca sendo feita in-place.
rebase db backup --out ./backups# recreate the volume on the new majorrebase db restore ./backups/<file>.dump