Migrar do Fly.io para a Zenifra
Este guia mostra como traduzir cada parte de um app do Fly.io para o console da Zenifra. No Fly.io, quase tudo está no fly.toml e na CLI fly, então o guia segue esse arquivo seção por seção. Para o roteiro geral, com cópia de dados e troca de DNS, veja Migrar para a Zenifra.
Mantenha o app do Fly.io no ar enquanto você sobe e valida a aplicação na Zenifra. Só destrua as Machines depois de trocar o DNS.
Como os conceitos se traduzem
| No Fly.io | Na Zenifra |
|---|---|
| App | Projeto HTTP |
| Machines do processo web | Instâncias Desejadas ou auto-scaling |
Grupos em [processes] | Um projeto por grupo |
| Build com Dockerfile | Imagem OCI ou Repositório Git com runtime |
internal_port | Porta |
[env] e secrets | Variáveis de Ambiente |
release_command | Início do Comando Start ou um passo manual |
| Checks HTTP | Healthcheck (a partir do plano Premium) |
Volumes em [[mounts]] | Armazenamento persistente |
| Machines agendadas | Jobs agendados |
| Fly Postgres ou Managed Postgres | PostgreSQL gerenciado |
| Upstash Redis | Cache gerenciado ou Chave-Valor |
| Tigris | Armazém |
Uma diferença importante: no Fly.io, auto_stop_machines pode desligar Machines sem tráfego. Na Zenifra, a aplicação fica ligada. No pagamento Por Hora, você paga pelo tempo em execução; veja Qual plano HTTP escolher.
1. Leia o fly.toml
Abra o fly.toml na raiz do repositório e anote cada seção:
app = "meu-app"
primary_region = "gru"
[build]
dockerfile = "Dockerfile"
[env]
NODE_ENV = "production"
[deploy]
release_command = "npx prisma migrate deploy"
[processes]
web = "node server.js"
worker = "node worker.js"
[http_service]
internal_port = 8080
processes = ["web"]
min_machines_running = 1
[[http_service.checks]]
path = "/health"
interval = "15s"
[[mounts]]
source = "dados"
destination = "/data"Seção do fly.toml | No console da Zenifra |
|---|---|
[build] dockerfile, image ou builder | Escolha da origem; veja a etapa 2 |
[env] | Variáveis de Ambiente |
[deploy] release_command | Veja Release command |
[processes] | Um projeto por grupo; o comando vai no Comando Start |
[http_service] internal_port | Porta |
[http_service] min_machines_running e fly scale count | Instâncias Desejadas |
[[http_service.checks]] path | Caminho da verificação, em Editar › Healthcheck da aplicação |
[[mounts]] destination | Diretório para Persistir |
[[vm]] memory e cpus | Escolha do plano |
[[statics]] | Sirva os arquivos pela própria aplicação |
primary_region | Não se aplica: a Zenifra roda no Brasil |
Confira também na CLI o que não está no arquivo:
fly scale show -a meu-app # quantidade e tamanho das Machines
fly secrets list -a meu-app # nomes dos secrets
fly certs list -a meu-app # domínios com certificado
fly volumes list -a meu-app # volumes2. Escolha a origem do projeto
O Fly.io constrói quase todos os apps a partir de um Dockerfile, muitas vezes gerado pelo fly launch. Na Zenifra você tem dois caminhos:
Opção A: repositório Git com runtime
Se o app é Node.js, Python, Bun, Go ou Java, você pode deixar o Dockerfile de lado e publicar pelo repositório. A Zenifra gera o ambiente de build sozinha e ignora o Dockerfile.
No console, clique em + Criar projeto, escolha Aplicação HTTP e o plano. Em Configurações Avançadas, escolha Repositório Git em Origem do Projeto, o Provedor Git GitHub (ou Forgejo), o repositório e a branch. Depois preencha:
| De onde vem | No console da Zenifra |
|---|---|
Linguagem e versão do FROM do Dockerfile, como node:22 | Runtime e Versão |
| Pasta do app no repositório | Diretório raiz |
Comando RUN de build do Dockerfile, como npm run build | Comando Build |
Grupo web em [processes] ou CMD do Dockerfile | Comando Start |
internal_port | Porta |
Veja os comandos de cada linguagem em Runtimes.
Opção B: imagem Docker
Para manter o mesmo Dockerfile, ou para linguagens como PHP, Ruby, Elixir e .NET, publique como Imagem Docker: construa a imagem, envie para um registry, como o GitHub Container Registry ou o Docker Hub, e informe a imagem no projeto.
docker build --platform linux/amd64 -t ghcr.io/minha-org/meu-app:1.0.0 .
docker push ghcr.io/minha-org/meu-app:1.0.0A imagem precisa incluir linux/amd64. Em Origem do Projeto, escolha Imagem Docker, informe a imagem e, se o registry for privado, as credenciais. Veja Deploy via imagem OCI e Registry privado. A GitHub Action faz a build e o deploy a cada push.
3. Recupere variáveis e secrets
As variáveis da seção [env] estão no fly.toml: copie direto.
Os secrets não podem ser lidos pela CLI: fly secrets list mostra só o nome e um digest de cada um. Pegue os valores na origem, como o gerenciador de senhas da equipe, o seu CI ou o .env usado no fly secrets import. Se não tiver outra cópia, os secrets estão nas variáveis de ambiente de uma Machine em execução, que você pode consultar pelo fly ssh console com cuidado para não deixar os valores no histórico do terminal.
Ao revisar as variáveis:
DATABASE_URLeREDIS_URLapontam para os bancos do Fly.io. Troque pelas URLs dos bancos na Zenifra.- Credenciais do Tigris (
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_ENDPOINT_URL_S3eBUCKET_NAME) mudam se você migrar os arquivos para o Armazém. - Variáveis do Fly.io, como
FLY_APP_NAME,FLY_REGION,FLY_MACHINE_IDePRIMARY_REGION, não existem na Zenifra. Se o código usa alguma, crie uma variável com o valor equivalente. - Se o código lê
PORT, criePORTcom o mesmo valor do campo Porta.
4. Ajuste o que muda entre as plataformas
Release command
No Fly.io, o release_command roda uma vez por deploy, em uma Machine temporária, antes de atualizar o app. Na Zenifra, coloque o comando no início do Comando Start, como npx prisma migrate deploy && node server.js. Ele passa a rodar sempre que uma instância inicia, então precisa ser idempotente. Para migrations longas ou com várias instâncias, rode a migration manualmente antes de publicar.
Processos
Cada grupo de [processes] vira um projeto na Zenifra, com o mesmo repositório ou a mesma imagem e o comando do grupo no Comando Start. Grupos que não recebem tráfego, como workers, usam Exposição Privado - sem rota pública.
Rede privada
No Fly.io, apps se falam por meu-app.internal ou meu-app.flycast. Na Zenifra, troque esses endereços pela URL do projeto de destino, *.clients.zenifra.com ou o domínio dele, e proteja as rotas internas com autenticação. Bancos e serviços gerenciados da Zenifra já têm as próprias URLs de conexão.
Tarefas agendadas
Machines criadas com fly machine run --schedule (hourly, daily, weekly ou monthly) e processos com cron dentro do container viram Jobs agendados. Na Zenifra, a Expressão cron define o horário exato, em UTC, como 0 6 * * * para todo dia às 06:00 UTC.
Arquivos estáticos
A seção [[statics]] fazia o Fly.io servir uma pasta da imagem direto. Na Zenifra, sirva esses arquivos pela própria aplicação, por exemplo com express.static no Node.js ou WhiteNoise no Django.
5. Migre bancos, volumes e arquivos
Postgres
Crie o PostgreSQL gerenciado na Zenifra antes da aplicação. Para copiar os dados, abra um túnel até o banco do Fly.io e use pg_dump na sua máquina.
Managed Postgres: em um terminal, abra o proxy. Por padrão, ele escuta em localhost:16380:
fly mpg proxy ID_DO_CLUSTERFly Postgres (app de Postgres): abra o proxy em uma porta livre:
fly proxy 15432:5432 -a meu-app-dbEm outro terminal, use o usuário, a senha e o banco da DATABASE_URL, trocando o host e a porta pelos do proxy:
pg_dump --format=custom --no-owner --no-acl \
"postgresql://usuario:senha@localhost:15432/banco" > dump.pgdump
pg_restore --no-owner --no-acl --jobs=4 \
--dbname="postgresql://usuario:senha@host:porta/banco?sslmode=verify-full&sslrootcert=system" \
dump.pgdumpCom o Managed Postgres, use a porta 16380 no lugar de 15432. Veja Migrar o banco de dados para validar a cópia.
Redis
Crie um cache gerenciado ou um banco Chave-Valor e troque a REDIS_URL. Dados de cache e de sessões costumam ser recriados pela aplicação. Se o Redis guarda filas, deixe os workers do Fly.io esvaziarem a fila antes da troca.
Volumes
No Fly.io, cada volume pertence a uma Machine e não é replicado. Para copiar os arquivos para a sua máquina:
fly ssh sftp get -R -a meu-app /data ./dados-do-volumeSe o app tem mais de uma Machine com volume, repita em cada uma com -s para escolher a Machine, porque cada volume tem dados diferentes.
Na Zenifra, ative o armazenamento persistente na criação do projeto, com o mesmo caminho em Diretório para Persistir, e envie os arquivos por uma rota de importação temporária protegida por senha. Esse caminho não pode ser alterado depois. Os dados do armazenamento persistente são replicados.
Tigris
O Armazém da Zenifra é compatível com S3, como o Tigris. Baixe os arquivos do bucket com a ferramenta que você já usa e envie para um bucket no Armazém com a AWS CLI ou o SDK S3. Depois troque endpoint, chaves e nome do bucket nas variáveis do projeto. Veja Conectar aplicações.
6. Valide e troque o DNS
- Abra a URL
*.clients.zenifra.comdo projeto e teste login, fluxos principais, e-mails, workers e webhooks. - Confira em Logs de build e nos logs da aplicação se não há erros.
- Siga o checklist do dia da troca: reduza o TTL do DNS, faça a cópia final dos dados, adicione o domínio na Zenifra e atualize o DNS.
- Depois que o DNS apontar para a Zenifra, remova os certificados do domínio no Fly.io com
fly certs remove. - Mantenha o app do Fly.io por alguns dias para rollback. Depois, destrua o app, os volumes e os bancos para parar a cobrança.
Problemas comuns
| Sintoma | Causa provável | Correção |
|---|---|---|
| URL pública não responde | Porta diferente da internal_port ou app escutando em localhost | Use a mesma porta e escute em 0.0.0.0 |
| Imagem não inicia | Imagem construída só para ARM | Construa com --platform linux/amd64 |
Erro ao conectar em *.internal | A rede privada do Fly.io não existe na Zenifra | Use a URL do projeto ou do banco na Zenifra |
| Variável vazia | Secret do Fly.io não foi copiado | Recupere o valor na origem e cadastre no projeto |
| Tabelas não existem | O release_command não roda na Zenifra | Rode as migrations; veja Release command |
| Worker parado | Grupo de [processes] sem projeto | Crie um projeto privado para cada grupo |
Precisa de ajuda?
Escreva para [email protected] com o seu fly.toml ou fale pelo WhatsApp +55 (11) 5199-3013.
Última atualização em
Migrar da Railway para a Zenifra
Passo a passo para trazer serviços da Railway para a Zenifra: onde copiar Root Directory, build e start commands e variáveis, como traduzir variáveis de referência, cron, volumes e bancos, e onde informar cada valor no console.
Solução de problemas
Problemas comuns de deploy, banco de dados, porta, domínio e operação na Zenifra.