Saia do Heroku Postgres sem perder dados
Siga isso no seu próprio servidor. Comece grátis, sem cartão
O Heroku parou de oferecer um plano Postgres gratuito há um tempo, e o plano mais barato que resta, o Essential-0, ainda te limita a 1 GB de armazenamento, 20 conexões e um limite de 4.000 tabelas por US$5 por mês. Tudo bem pra uma aplicação de teste. Não tá bem pra nada que de fato cresceu, e não tá bem pelo preço depois que você compara com rodar o mesmo banco de dados em outro lugar. A parte que trava a maioria das pessoas não é a decisão, é o medo de quebrar os dados na saída: perder linhas, restaurar num encoding errado, ou fazer o cutover antes do novo banco de dados estar realmente pronto. No final deste guia você vai ter uma cópia completa e verificada dos seus dados do Heroku Postgres rodando num banco de dados novo, a sua aplicação apontando pra ele, e o antigo ainda lá como uma rede de segurança até você ter certeza.
O que você vai precisar
- O Heroku CLI instalado e logado (
heroku login), com acesso à aplicação cujo banco de dados você está migrando. - Um servidor Postgres de destino. Este guia usa um droplet Basic da DigitalOcean a US$6/mês (1 vCPU, 1 GiB de RAM, 25 GB SSD) rodando Postgres no Docker, que acomoda confortavelmente um banco de dados que estava vivendo num plano Heroku Essential-0 de 1 GB. Use uma máquina maior se os seus dados forem maiores.
pg_dumpepg_restorena sua máquina, com a versão do Postgres igual ou mais nova que a do seu banco de dados Heroku. O Postgres.app ou o pacotepostgresql-clientno seu sistema operacional te dá os dois.- Uma janela de manutenção curta não é estritamente necessária, mas ajuda escolher um horário tranquilo, já que você vai reapontar o
DATABASE_URLno meio do caminho. - Cerca de 10 a 15 minutos, mais se o seu banco de dados for grande ou a sua conexão for lenta.
Passo 1: capture um backup novo no Heroku
Não restaure a partir de qualquer backup automático que o Heroku tenha disponível por acaso. Tire um agora, pra você saber exatamente qual momento no tempo você está migrando.
heroku pg:backups:capture --app your-app-name
Isso dispara um backup novo do lado do Heroku, no formato de dump compactado customizado do Postgres. Pode levar de poucos segundos a vários minutos dependendo do tamanho do seu banco de dados. Acompanhe com:
heroku pg:backups --app your-app-name
Você deve ver uma linha pro backup que você acabou de capturar, com status Completed e um tamanho mais ou menos equivalente ao que você esperaria pro seu banco de dados. Se ainda disser Pending ou Running, espere e cheque de novo antes de continuar, um backup que ainda está capturando vai falhar ao tentar baixar.
Passo 2: baixe o backup
Depois que a captura terminar, baixe ele pra sua máquina:
heroku pg:backups:download --app your-app-name
Isso salva um arquivo chamado latest.dump (ou o ID específico do backup se você passou um) no diretório atual. Se você preferir entregar o arquivo pra outra coisa em vez de baixar você mesmo, heroku pg:backups:url --app your-app-name te dá uma URL pública temporária pro mesmo dump, válida por cerca de uma hora.
Você deve ver um arquivo latest.dump no disco, com um tamanho na mesma faixa do que heroku pg:backups reportou.
Passo 3: configure o banco de dados Postgres de destino
Se você estiver migrando pra uma instância Postgres self-hosted em vez de outro provedor gerenciado, um Docker Compose pequeno te dá um banco de dados rodando rápido:
services:
postgres:
image: postgres:16.4-alpine
restart: unless-stopped
environment:
POSTGRES_USER: appuser
POSTGRES_PASSWORD: change-me
POSTGRES_DB: appdb
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
volumes:
pgdata:
Suba com:
docker compose up -d
Você deve ver o container rodando (docker compose ps) e conseguir conectar com psql -h your-server-ip -U appuser -d appdb. Se você estiver apontando pra um Postgres gerenciado de outro provedor em vez disso, só confirme que você tem a connection string dele e que ele aceita conexões do seu IP antes de continuar.
Passo 4: restaure o dump no banco de dados novo
O pg_restore lê o mesmo formato customizado em que o backup do Heroku já está, então não existe etapa de conversão:
pg_restore --verbose --clean --no-acl --no-owner \
-h your-server-ip -U appuser -d appdb latest.dump
--clean apaga qualquer objeto conflitante antes de recriá-los, o que importa se você já rodou migrations contra um banco de dados vazio e ele tem tabelas que de outra forma colidiriam com a restauração. --no-acl e --no-owner ignoram referências de role e permissão específicas do Heroku que não existem no seu novo servidor e de outra forma gerariam erros que você pode ignorar com segurança.
Você deve ver um longo fluxo de linhas CREATE TABLE, CREATE INDEX e COPY passando sem erros fatais no final. Alguns avisos de already exists ou does not exist, skipping são normais e não são sinal de que algo deu errado.
Passo 5: verifique se os dados de fato correspondem
Antes de tocar no DATABASE_URL, cheque se o que veio é completo. Compare a contagem de linhas tabela por tabela entre o Heroku e o banco de dados novo:
SELECT schemaname, relname, n_live_tup
FROM pg_stat_user_tables
ORDER BY relname;
Rode isso contra os dois bancos de dados (conecte ao Heroku com heroku pg:psql --app your-app-name) e compare os resultados. Pra qualquer coisa que a sua aplicação trata como financeira ou legalmente importante, confira manualmente um punhado de linhas específicas por chave primária em vez de confiar só nas contagens.
Você deve ver contagens de linhas correspondentes pra toda tabela, sem nenhuma tabela presente de um lado e faltando do outro.
Passo 6: aponte a sua aplicação pro banco de dados novo
Depois de confiante que os dados correspondem, atualize o que quer que guarde o seu DATABASE_URL — o seu arquivo .env, as config vars do seu novo host, o seu gerenciador de secrets — pra nova connection string, e refaça o deploy. Se o seu destino novo precisa de sslmode=require ou algo parecido, defina isso na connection string agora em vez de descobrir em tempo de execução.
Você deve ver o próximo deploy da sua aplicação subir com sucesso e ler/escrever contra o banco de dados novo. Acompanhe os logs de erro por alguns minutos depois do deploy pra qualquer coisa relacionada a conexão.
Passo 7: mantenha o Heroku por perto por um tempo, depois desative
Não apague a aplicação do Heroku nem reduza o banco de dados dela no momento em que o cutover parecer bem. Deixe rodando, intocada, por um dia ou dois como um plano B pro qual você pode voltar se algo surgir que a sua checagem de contagem de linhas perdeu. Depois de confiante, reduza o dyno e o banco de dados do Heroku, e então remova eles.
O caminho mais rápido: faça isso com a Railyard
Tudo acima é o caminho manual, que vale conhecer porque é o que realmente está acontecendo por baixo de qualquer ferramenta que afirma fazer isso por você. A Railyard tem dois jeitos de pular a maior parte disso.
Se você está movendo a aplicação inteira, não só o banco de dados, Applications → Import lê a sua aplicação direto do Heroku usando um token de API: config vars, a formation (os seus tipos de processo e as contagens deles), e qualquer add-on de Postgres, Redis, MySQL ou Memcached vem automaticamente, junto com domínios customizados. Você ainda adiciona a URL do Git você mesmo, já que a API do Heroku não expõe o seu repositório. A importação do Heroku está atualmente em beta, o que significa que ainda não foi testada contra contas reais suficientes pra ser chamada de totalmente comprovada, então a Railyard te mostra o plano pra sua aplicação específica antes de qualquer coisa acontecer, e você revisa antes de confirmar. O banco de dados em si vem através de streaming de um dump direto do seu DATABASE_URL do Heroku pro novo banco de dados gerenciado, e a aplicação antiga do Heroku continua servindo tráfego até a nova URL responder e você mesmo mudar o DNS.
Se você só quer mover o banco de dados e vai manter a sua aplicação e pipeline de deploy onde estão, o fluxo independente Import data faz a metade do banco de dados desse mesmo trabalho por conta própria: cole a URL de conexão do seu Heroku Postgres (a mesma que heroku config:get DATABASE_URL te dá), e a Railyard puxa os dados direto dela. Ela faz backup do que quer que esteja no banco de dados de destino primeiro, então se a importação falhar ou você cancelar no meio, os seus dados existentes são restaurados automaticamente em vez de ficarem parcialmente sobrescritos. Se você escolher fazer deploy depois da importação terminar, ela roda as migrations da sua aplicação pra você como última etapa.
De qualquer jeito, a saída do seu heroku config -s cola direto em Variables → Bulk import da Railyard, então as suas variáveis de ambiente não precisam ser digitadas de novo manualmente.
Detalhes completos dos dois fluxos: Importing from another platform e Databases and backups. Se você ainda está decidindo se vai migrar, o passo a passo completo está em Migrating from Heroku, e o nosso verificador de conta do Heroku te diz mais ou menos quanto você está pagando hoje versus em outro lugar.
Resolução de problemas
heroku: command not found ou erros de autenticação. Confirme que o CLI está instalado e rode heroku login de novo; uma sessão velha ou expirada aparece como um erro de permissão em comandos que de outra forma parecem corretos.
O download do backup falha ou retorna um arquivo vazio. Isso quase sempre significa que a captura do Passo 1 ainda não tinha de fato terminado. Cheque heroku pg:backups --app your-app-name de novo e espere por Completed antes de baixar ou pedir uma URL.
Erros do pg_restore sobre objetos que já existem. Isso acontece quando você restaura num banco de dados que já tem tabelas conflitantes, geralmente um onde migrations já rodaram. Use --clean como mostrado acima, ou restaure num banco de dados genuinamente vazio e rode as suas próprias migrations em vez do schema do dump.
Erros de conexão SSL ou sslmode. O Heroku Postgres exige uma conexão SSL pra acesso direto via pg_dump/psql; se você estiver fazendo dump direto do Heroku em vez de a partir de um arquivo de backup baixado, adicione ?sslmode=require na connection string. O seu banco de dados de destino pode ter o problema oposto, recusando SSL totalmente numa instalação local nova, caso em que você remove o parâmetro desse lado.
A sua aplicação ainda está escrevendo no banco de dados antigo depois do cutover. Isso quase sempre é um DATABASE_URL em cache, um valor de gerenciador de secrets que não refez o deploy de fato, ou um worker em segundo plano que não reiniciou junto com o processo web. Cheque cada tipo de processo separadamente, não só o que serve tráfego HTTP.
Perguntas frequentes
Isso causa downtime? Não se você seguir a ordem acima. A aplicação do Heroku continua servindo tráfego durante as etapas de backup, restauração e verificação; o único momento que importa é quando você troca o DATABASE_URL, que normalmente é um único redeploy.
O que acontece com a minha aplicação do Heroku enquanto eu migro? Nada, ela continua rodando exatamente como antes. A captura do backup lê de um processo de snapshot parecido com uma réplica e não bloqueia as suas tabelas de produção.
Eu consigo migrar sem o Heroku CLI? Só parcialmente. Você consegue pegar uma connection string nas Config Vars da sua aplicação no painel do Heroku e rodar pg_dump direto contra ela, mas capturar e baixar um backup especificamente exige o CLI (ou a Railyard puxando direto da URL em tempo real, que não precisa de CLI nenhum).
Eu preciso que as versões do Postgres correspondam exatamente? Não, mas o seu destino deveria ser da mesma versão principal ou mais nova. Restaurar um dump mais novo do Heroku Postgres num destino mais antigo pode falhar em funcionalidades que ainda não existiam na versão mais antiga.
E se o meu banco de dados for maior do que cabe num ciclo rápido de backup/restauração? O processo é o mesmo, só leva mais tempo. Reserve mais tempo especificamente pros Passos 1 e 4, e considere rodar a restauração numa máquina com mais RAM temporariamente se o pg_restore estiver lento.
Qualquer caminho que você escolha, manual ou pela Railyard, a disciplina que realmente protege os seus dados é a mesma: capture antes de tocar em qualquer coisa, verifique antes de fazer o cutover, e não apague a cópia antiga até você ter tido tempo de ter certeza.
Por que escolhem o Railyard para isso
- Sua própria conta de cloud. Faça deploy no DigitalOcean, Hetzner, Vultr, AWS, OVHcloud, ou em qualquer servidor Linux via SSH, cobrado direto pelo seu provedor, sem markup.
- Um preço fixo por equipe. O Free é grátis para sempre: um servidor mais um servidor de banco de dados, três apps, sem cartão. O Pro custa US$19 por mês para até cinco servidores e apps ilimitados. O Team custa US$49 por mês com servidores ilimitados. Sem cobrança por app, por deploy, por assento ou por gigabyte.
- Templates de um clique. Faça deploy de apps open source, como bancos de dados, analytics e ferramentas de dev, no seu próprio servidor em um clique.
- Bancos de dados gerenciados. Postgres, MySQL e Redis com backups programados, verificados por restauração, e recuperação point-in-time.
- Push-to-deploy. Faça deploy a partir do GitHub, GitLab ou da CLI, com HTTPS automático e releases sem downtime.