---
title: "Backup e restauração"
description: "Backup online pelo comando embutido na imagem: gere um ZIP verificado sem parar o serviço, confira-o e restaure em uma raiz de dados limpa."
---

# Backup e restauração

O backup do Notarium é um **comando embutido na imagem**, não uma cópia de arquivos feita de fora. O `notarium backup` monta um ZIP lógico enquanto o serviço segue atendendo: a leitura continua disponível o tempo todo, e a escrita só é segurada em dois checkpoints curtos. Basta ter o Docker e um contêiner rodando — não é preciso parar o serviço.

> [!danger] Nunca copie um `meta.db` em uso
> `cp /data/meta.db` não é backup. O banco de metadados roda em modo WAL: linhas já commitadas podem ainda estar no `meta.db-wal`, e arquivos copiados um a um não formam um instantâneo de um único momento. Uma instância restaurada a partir dessa cópia perde dados em silêncio.

## Fazer um backup

```bash
docker compose exec -T notarium backup > notarium-20260731.zip
```

Isto é um backup de verdade, não um teste do tipo "será que funciona": o comando monta o arquivo de backup, passa-o pelo próprio verificador e só então envia os bytes para o stdout. A flag `-T` é **obrigatória** — sem ela o Compose aloca um pseudoterminal, e um stream binário não atravessa um deles intacto: o resultado é um arquivo de backup corrompido. O `docker exec` puro não aloca pseudoterminal por padrão, então lá a flag não é necessária. A saída padrão fica reservada estritamente para os bytes do ZIP; o progresso e o resumo final vão para o stderr e nunca contaminam o arquivo de backup.

## Para uma tarefa agendada

O redirecionamento com `>` tem uma armadilha: o arquivo com o nome final é criado **antes** de o comando fazer qualquer trabalho. Se o Docker ou o backup morrer no meio do caminho, sobra um arquivo com o nome certo e conteúdo incompleto. Abaixo vai uma sequência de publicação segura contra falhas: escreva em um arquivo temporário, force a gravação em disco e publique com um hard link atômico.

```bash
backup="notarium-$(date -u +%Y%m%dT%H%M%SZ).zip"
partial="${backup}.partial.$$"
set -eu
umask 077
committed=0
cleanup() { test "$committed" -eq 1 || rm -f "$partial"; }
trap cleanup EXIT
docker compose exec -T notarium backup > "$partial"
sync -f "$partial"
sync -f "$(dirname "$backup")"
committed=1
ln "$partial" "$backup"
if sync -f "$(dirname "$backup")"; then
  if rm "$partial"; then
    sync -f "$(dirname "$backup")" ||
      echo "backup warning: final is durable; partial cleanup fsync failed" >&2
  else
    echo "backup warning: final is durable; retaining recovery partial $partial" >&2
  fi
else
  echo "backup warning: final is visible; retaining durable recovery partial $partial" >&2
fi
trap - EXIT
```

O `set -e` impede que o arquivo de backup seja publicado se o Docker ou o backup retornarem erro. O nome temporário carrega o PID, então duas tarefas rodando ao mesmo tempo não brigam pelo mesmo arquivo. O arquivo temporário e o diretório dele são gravados em disco **antes** do ponto de publicação, e a publicação em si é um hard link atômico que nunca sobrescreve: duas tarefas mirando o mesmo nome de destino não têm como se atropelar.

Falhas **depois** do ponto de publicação são avisos, não alarmes falsos: o arquivo final está no lugar e, em casos ambíguos, o temporário é mantido como cópia reserva. Até um código de saída não-zero ambíguo do `ln` preserva essa cópia — o link pode ter sido criado logo antes de o processo ser interrompido. O `umask 077` deixa o arquivo de backup legível somente para o dono. Mantenha o temporário e o final no mesmo sistema de arquivos.

Para um contêiner subido com `docker run` puro sob o nome `notarium`, tudo acima vale igual — muda uma linha:

```bash
docker exec notarium backup > "$partial"
```

> [!tip] Se o contêiner já enxerga o seu diretório de backups
> O script auxiliar acima existe porque o transporte via stdout não consegue publicar um arquivo sozinho. Quando o diretório dos arquivos de backup já está montado — um compartilhamento de backup, NFS, um volume scratch ao lado de uma raiz somente leitura —, o `backup --output /path/archive.zip` faz o mesmo trabalho: escreve em um arquivo temporário ao lado do destino, grava em disco, verifica e publica com um hard link atômico que nunca sobrescreve; em caso de falha, não deixa nada para trás sob o nome de destino. Aí nenhum script auxiliar é necessário, e o stdout carrega um único resumo JSON.

> [!note] O backup precisa de um contêiner rodando
> O comando é executado com `docker exec` dentro do contêiner que já está atendendo — não como um contêiner separado: tirar um instantâneo consistente exige coordenação com a aplicação em execução.

## Quando um backup pode falhar

O backup foi construído para que **você nunca fique, sem perceber, com um arquivo de backup inconsistente**: se o instantâneo não puder ser tirado, o comando termina com erro e não publica nada. Dois casos em que isso acontece:

- **Um fluxo contínuo de edições.** Os dados precisam ficar parados enquanto o arquivo de backup é montado; uma escrita que se sobreponha dispara uma nova tentativa e, sob um fluxo interminável de edições, o comando desiste com erro. Na prática, isso aparece em uma instância movimentada — é só tentar de novo mais tarde.
- **Uma importação ou exportação longa em andamento.** O backup precisa de duas pausas bem curtas na escrita, e uma tarefa longa não cabe nelas. Não agende um backup na mesma janela de uma importação em massa.

Nenhum dos dois casos prejudica o serviço: a fila de escrita é liberada na hora, e o comando do operador nunca segura a aplicação. De um jeito ou de outro, a leitura comum fica disponível durante todo o backup.

## O que vai dentro do arquivo de backup

| Caminho no arquivo de backup | O que é |
|---|---|
| `data/meta.db` | Contas, sessões, participação, identificadores estáveis, histórico de versões e estado das tarefas. |
| `data/spaces/` | A fonte da verdade em Markdown, incluindo a memória do agente e os arquivos marcadores de projeto. |
| `data/jobs/` | Artefatos prontos e uploads duráveis de importação. |
| `manifest.json` | Versão do formato, marca de tempo, o conjunto exato de diretórios, além de tamanho, mtime e SHA-256 de cada arquivo. |

O diretório derivado `data/engine/` **não** entra: os índices são reconstruídos a partir dos arquivos depois de uma restauração. Dos arquivos incompletos, só os internos ficam de fora — os temporários da escrita atômica de notas, importações com upload pela metade e pedaços de artefatos de exportação. Arquivos comuns do usuário cujo nome termina em `.part` permanecem no arquivo de backup: eles são legítimos.

> [!warning] O arquivo de backup é dado sensível
> O ZIP contém contas e estado de sessão vindos do banco de metadados. Trate-o como segredo: o `umask 077` no trecho acima deixa cada novo arquivo de backup legível somente para o dono.

## Verificação

A verificação não altera nada e deve fazer parte de toda rotina de backup:

```bash
docker compose exec -T notarium backup verify < notarium-20260722.zip

# para docker run puro:
docker exec -i notarium backup verify < notarium-20260722.zip
```

Em caso de sucesso, o comando imprime um único resumo JSON e sai com código zero. São rejeitados: caminhos inseguros e duplicados, arquivos ausentes do manifesto, um conjunto de diretórios que não bate exatamente, divergências de tamanho e de hash, metadados de tempo inválidos, estouro de limites e falha na verificação de integridade do SQLite. O diretório de dados em uso não é lido nem alterado nesse processo.

> [!important] Somas de verificação não são assinatura
> Os hashes pegam corrupção acidental, mas não protegem contra adulteração: quem conseguir trocar tanto o conteúdo quanto o manifesto passa na verificação. Trate o armazenamento dos backups como estado confiável de acesso restrito, ou acrescente assinatura ou criptografia na camada que transporta o arquivo de backup.

## Restauração

A restauração é uma **operação de desastre, feita offline**. Ela só aceita uma raiz de dados limpa e vazia, e nunca se mistura a uma instância existente nem a sobrescreve.

Prepare um volume novo e aponte o serviço para ele **antes** de começar:

```bash
set -eu
docker compose stop notarium
# tire o volume antigo do caminho; monte um /data vazio no compose
docker compose run --rm --no-deps -T notarium restore \
  < notarium-20260722.zip
docker compose up -d --force-recreate --no-deps notarium
```

Depois disso, o contêiner precisa ser recriado, não apenas iniciado: o `docker compose start` traria de volta o contêiner antigo com a configuração de montagem antiga. Guarde o volume antigo até ter conferido a instância restaurada.

A restauração verifica o arquivo de backup inteiro antes de instalar qualquer coisa. Se o processo for interrompido no meio da instalação, fica um marcador explícito: considere aquele destino de uso único e restaure em um novo destino vazio, em vez de insistir ou tentar mesclar.

O que conferir depois de uma restauração:

1. Entrar com uma conta do backup.
2. Abrir alguns espaços e confirmar que endereços e identificadores estão intactos.
3. Abrir uma nota que você tinha editado e olhar o histórico dela.
4. Conferir as tarefas de importação e exportação cujos uploads ou artefatos importam para você.

> [!note] Compatibilidade com o esquema do banco de metadados
> O banco de metadados que você está restaurando precisa trazer um registro de migrações que o build de destino aceite. Um banco não vazio e sem registro falha fechado — a restauração não adivinha a versão dele nem carimba uma por conta própria. Veja [Banco de dados](/docs/self-hosting/database/).

## Limites

O comando embutido só suporta o layout canônico: uma única raiz de dados e o banco de metadados em um arquivo SQLite. Se `META_DB_URL` apontar para o Postgres, ou se as notas ficarem fora de `DATA_DIR`, o comando **falha fechado** em vez de entregar um arquivo de backup pela metade — nesse caso, use as ferramentas próprias do seu banco somadas a instantâneos dos diretórios montados.

Os arquivos intermediários do backup e da verificação ficam em `/tmp` por padrão. Um backup em streaming se verifica antes de publicar, então pode precisar temporariamente de espaço para o arquivo de backup mais dois estágios expandidos; uma verificação avulsa precisa do arquivo de backup mais um. A restauração faz buffer do stream de entrada no diretório temporário (scratch), mas expande o arquivo de backup direto na própria raiz de dados nova. Se o sistema de arquivos raiz do contêiner estiver montado somente leitura, ou se você tiver muitos dados, aponte `NOTARIUM_BACKUP_TMPDIR` para um diretório montado com permissão de escrita.

A entrada comprimida e a expandida têm teto de 64 GiB e um milhão de entradas; os nomes, as estruturas internas do ZIP e o `manifest.json` têm um limite de memória separado, de 32 MiB. Instalações grandes e confiáveis podem elevar `NOTARIUM_BACKUP_MAX_BYTES`, `NOTARIUM_BACKUP_MAX_ENTRIES` e `NOTARIUM_BACKUP_MAX_METADATA_BYTES` — veja [Variáveis de ambiente](/docs/reference/environment-variables/).

## A seguir

- [CLI da imagem](/docs/self-hosting/cli/) — o contrato completo de comandos, streams e códigos de saída.
- [Banco de dados](/docs/self-hosting/database/) — o que exatamente o banco de metadados guarda e por que ele nunca pode ficar de fora de um backup.
- [Produção](/docs/self-hosting/production/) — proxy reverso, o invariante de instância única, operação do dia a dia.
