Skip to content
AzizARSLNPublic

About

Self-hosted PostgreSQL migration and backup tool with a local web console

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

DBFerry

Python MCP Dependencies Platform Console

Self-hosted PostgreSQL migration and backup tool with a local web console and an MCP server.

DBFerry moves a PostgreSQL database from one place to another without hand-writing pg_dump / pg_restore chains. It takes care of the parts that usually go wrong: stopping the right services in the right order, keeping a safety backup, verifying the transfer with SHA-256, restoring into a clean database, and then reporting what actually happened.

Remote hosts are reached over SSH (Paramiko). The local machine is driven through Git Bash, so the same job definition works whether the target is a server in a datacenter or PostgreSQL installed on the laptop in front of you.

There is no server to deploy and no database of its own — everything runs from the machine you launch it on.


Job types

Key Direction What it does
file_to_server local .dump → remote host Restores a dump file into a Docker PostgreSQL on a remote server
file_to_local local .dump → this machine Restores a dump file into the PostgreSQL installed locally
server_to_server live copy Dumps on the source, pulls it down, restores on the target
server_to_file remote host → local disk Backs a remote database up to .dump + .sha256
local_to_file this machine → local disk Backs a local database up to .dump + .sha256

Backup jobs are read-only. Restore jobs can drop and recreate the target database, depending on the chosen restore mode.

Features

  • Plan before action. Every job produces a numbered step list first. Nothing is touched until you approve it. The same list is the progress bar during the run.
  • Live output. Log lines stream to the console over Server-Sent Events, including the exact commands that were executed and their stdout/stderr.
  • Integrity checks. Dumps are written in PostgreSQL custom format (pg_dump -Fc) and checksummed with SHA-256. A .sha256 sidecar file is written next to each backup and verified on the way back in.
  • Maintenance window handling. Optionally stops the application containers before dumping a live database, then brings them back when the job is done. Container lists are configured per profile.
  • Docker or direct. PostgreSQL can live inside a container (commands run through docker exec) or be installed on the host (commands run directly). Both are supported for remote and local targets.
  • Safety backup. Before a destructive restore, the current database is dumped to data/backups/<name>/ so there is a way back.
  • Post-restore verification. Table counts, schema_migrations version and your own SQL | expected checks are validated after the restore.
  • Profiles and history. Hosts are stored once and reused; every run is appended to a local history file with its status and duration.
  • MCP server. The same engine is exposed to AI agents as a Model Context Protocol server: dry-run by default, destructive calls gated behind a confirmation token, and an optional read-only mode that removes restore entirely.

How it works

PLAN ──► approve ──► RUN ──► REPORT
                      │
                      └── events → console (CLI) / SSE (web)

A live copy is just a pipeline of shell steps, each one logged as it runs:

pg_dump -Fc ──► SHA-256 ──► transfer (SFTP or local copy) ──► DROP + CREATE ──► pg_restore ──► verify

A read-only backup stops at the checksum step and writes the file to disk.

Requirements

  • Python 3.10 or newer (the MCP server SDK requires it)
  • paramiko for SSH, and mcp for the AI-agent server
  • PostgreSQL client tools — pg_dump, pg_restore, psql, sha256sum
    • in Docker mode they are expected to exist inside the container
    • in direct mode they must be reachable on PATH (or pointed to with the profile's PostgreSQL bin field / DBFERRY_PG_BIN)
  • For remote jobs: SSH access to the host, and permission to run docker on it
  • Windows only: Git for Windows. Local jobs run their commands through bash.exe; the detection can be overridden with DBFERRY_BASH.

Installation

git clone https://github.com/AzizARSLN/DBFerry.git
cd DBFerry
python -m pip install -r requirements.txt

The repository is also a standard Python package, so it can be installed into its own environment with the dbf and dbferry-mcp entry points:

pipx install .          # or:  uv tool install .
dbf web

Usage

Web console

python dbf.py web

Opens http://127.0.0.1:8787 in your browser. Pick a job type from the tabs, fill in the connection details, use Planı Göster to preview the steps, then Süreci Başlat to run it. Everything is logged in the right-hand panel while it runs.

python dbf.py web --port 9000 --no-browser

Command line

python dbf.py run --spec job.json            # plan, ask for confirmation, run
python dbf.py run --spec job.json --yes      # skip the confirmation prompt
python dbf.py inspect --spec job.json        # read-only environment check
python dbf.py profiles                       # list saved hosts
python dbf.py history --limit 20             # recent runs

Docker

docker build -t dbferry .
docker run --rm -i -v "$PWD/data:/data" dbferry                     # MCP server over stdio
docker run --rm -p 8787:8787 -v "$PWD/data:/data" dbferry \
  web --host 0.0.0.0 --no-browser                                   # web console

The image keeps its state in the /data volume (DBFERRY_DATA_DIR=/data), so profiles and backups survive between runs. pg_dump / pg_restore still have to exist on the machine that owns the database — the container drives them over SSH or the local shell.

Example job spec

run and inspect take a JSON file. A backup of a local database looks like this:

{
  "type": "local_to_file",
  "source_db": "appdb",
  "local_db_host": "127.0.0.1",
  "local_db_port": 5432,
  "local_db_user": "postgres",
  "local_db_password": "secret",
  "local_pg_mode": "local",
  "local_pg_bin": "C:\\Program Files\\PostgreSQL\\17\\bin",
  "local_copy_dir": ""
}

And a restore onto a remote server:

{
  "type": "file_to_server",
  "source_dump": "C:\\backups\\appdb.dump",
  "target_profile": "a1b2c3d4",
  "target_db": "appdb",
  "mode": "replace",
  "take_safety_backup": true,
  "stop_services": true,
  "start_services": true,
  "verify": true,
  "expect_migration": "V068"
}

target_profile is the id of a saved profile (see python dbf.py profiles). Leave local_copy_dir empty to write backups under data/backups/<database>/.

AI agents (MCP)

DBFerry also runs as a Model Context Protocol server, so an assistant such as Claude Code, Cursor or Codex can back up, transfer and verify databases through the same plan → approve → run → verify pipeline the console uses.

python dbf.py mcp                                     # stdio (default, for local agents)
python dbf.py mcp --transport streamable-http --port 8765
python dbf.py mcp --transport sse --port 8765

The server speaks the same data/ directory as the CLI: profiles are read from data/profiles.json and job history is appended to data/history.jsonl.

Registering the server

The server is started by the client, so a full clone is not required — point the client at the mcp subcommand. Claude Desktop / Claude Code (claude_desktop_config.json or .mcp.json):

{
  "mcpServers": {
    "dbferry": {
      "command": "python",
      "args": ["dbf.py", "mcp"],
      "cwd": "/absolute/path/to/DBFerry",
      "env": { "DBFERRY_DATA_DIR": "/absolute/path/to/DBFerry/data" }
    }
  }
}

Any command that runs python dbf.py mcp works; the cwd (or DBFERRY_DATA_DIR) is what tells the server which profiles to load.

Tools

Tool Kind Purpose
db_profiles read List configured profiles (never returns passwords)
db_inspect read Environment check: databases, size, table count, migration version, disk
db_plan read Step list and risk flags for a backup or restore operation
db_backup read Dump a database to a local .dump + .sha256
db_restore destructive Restore a dump into a target; gated by dry-run and a confirmation token
db_verify read Prove the result: existence, size, tables, migration version, SQL checks
db_job_status read Progress and recent log lines for running and finished jobs

Safety model

The server is built so an agent cannot destroy a database by accident:

  • Dry run is the default. db_restore returns the step list and executes nothing until it is called with dry_run=false.
  • Destructive steps need a token. With mode="replace" the call is rejected unless confirm equals the target database name — the agent has to echo the exact name of the database it is about to drop.
  • Read-only lockdown. Setting DBFERRY_READONLY=1 removes db_restore from the server entirely, leaving only backup and verification tools.
  • No credentials in arguments. Connection details come from the locally stored profile; passwords are never tool parameters and never returned in tool results.
  • Safety backup first. Every restore dumps the current database to data/backups/<name>/ before touching it.
  • Verified transfers. Dumps are SHA-256 checksummed on write and checked on read.

Environment

Variable Default Effect
DBFERRY_DATA_DIR ./data Where profiles, history and backups live
DBFERRY_READONLY unset 1 disables every data-changing tool
DBFERRY_PG_BIN unset PostgreSQL bin directory for direct (non-Docker) targets
DBFERRY_BASH auto bash.exe used for local jobs on Windows

Relation to postgres-mcp

The official PostgreSQL MCP servers expose a SQL connection: they run queries and let the model reason about a schema. DBFerry's server exposes operations: it plans, performs and verifies a database migration end to end, and it is deliberately unable to serve ad-hoc SQL. Reach for a SQL-oriented server to inspect data; reach for DBFerry to move a database and prove the move succeeded.

Project layout

DBFerry/
├── dbf.py                 # CLI entry point (web, mcp, run, inspect, profiles, history)
├── pyproject.toml         # packaging metadata and console entry points
├── requirements.txt
├── Dockerfile             # container image for the web console and MCP server
└── dbf_ferry/
    ├── model.py           # ServerProfile, JobSpec, job/restore mode definitions
    ├── store.py           # profiles.json + history.jsonl
    ├── events.py          # event bus used by both the CLI and the web console
    ├── sshx.py            # SSH/SFTP layer and host key handling
    ├── localx.py          # local machine layer (drives Git Bash, mirrors the SSH API)
    ├── pg.py              # PostgreSQL operations: dump, restore, verify, docker exec
    ├── jobs.py            # orchestration: plan, run, step tracking, reporting
    ├── mcp/
    │   └── server.py      # MCP server: 7 tools over stdio / SSE / streamable HTTP
    └── web/
        ├── app.py         # stdlib HTTP server + JSON/SSE API
        └── static/        # console UI (index.html, app.js, style.css)

HTTP API

The web console is a thin client over a small JSON API. It is useful for scripting too.

Method Endpoint Purpose
GET /api/meta version, job types, data directory
GET /api/profiles saved hosts (passwords masked)
POST /api/profiles create or update a profile
POST /api/profiles/delete remove a profile
POST /api/hostkey fetch an SSH host key fingerprint
POST /api/local/dbs connect to the local PostgreSQL and list databases
POST /api/plan build and return the step list for a spec
POST /api/inspect start a read-only environment check
POST /api/run start a job
GET /api/jobs/<id>/events Server-Sent Events stream for a running job
POST /api/jobs/<id>/cancel request cancellation
GET /api/history recent runs

Security notes

  • Profiles — including SSH and database passwords — are kept in data/profiles.json on the machine running DBFerry. Passwords are currently stored in plain text there, so treat that file as a secret. It is listed in .gitignore and is never sent to the browser: the API returns masked profiles with has_password flags instead.
  • The web console has no authentication and binds to 127.0.0.1 by default. Do not expose it to a network you do not control.
  • In replace mode a restore drops the target database before recreating it. Leave take_safety_backup enabled unless you have your own copy.
  • The MCP server inherits these rules. db_restore is dry-run by default, requires confirm=<database> for replace, and disappears entirely when DBFERRY_READONLY=1.

Notes and limitations

  • Service startup waits for a container to reach running, not healthy. Stacks with slow dependencies are reported as soon as the container is up, so give them a moment before pointing traffic at the API.
  • schema_migrations verification is skipped (with a warning) when the table does not exist in the configured schema.
  • Local jobs on Windows need Git Bash; on Linux and macOS the system shell is used.
  • Environment overrides: DBFERRY_DATA_DIR, DBFERRY_READONLY, DBFERRY_PG_BIN, DBFERRY_BASH.

Türkçe

Yerel web konsolu ve MCP sunucusu olan, kendi sunucunuzda çalışan PostgreSQL taşıma ve yedekleme aracı.

DBFerry, bir PostgreSQL veritabanını elle pg_dump / pg_restore zinciri yazmadan başka bir ortama taşır. Genelde sorun çıkaran işleri üstlenir: servisleri doğru sırayla durdurur, önce güvenlik yedeği alır, aktarımı SHA-256 ile doğrular, temiz bir veritabanına geri yükler ve sonunda ne yaptığını raporlar.

Uzak makinelere SSH (Paramiko) ile, yerel makineye Git Bash üzerinden erişir. Aynı iş tanımı hem veri merkezindeki bir sunucu hem de yanı başınızdaki PostgreSQL için çalışır. Kurulacak bir sunucu ya da ayrı bir veritabanı yoktur.

İş tipleri

Anahtar Yön Açıklama
file_to_server yerel .dump → uzak sunucu Döküm dosyasını uzaktaki Docker PostgreSQL'e geri yükler
file_to_local yerel .dump → bu bilgisayar Döküm dosyasını yereldeki PostgreSQL'e geri yükler
server_to_server canlı kopya Kaynakta döküm alır, indirir, hedefte geri yükler
server_to_file uzak sunucu → yerel disk Uzaktaki veritabanını .dump + .sha256 olarak yedekler
local_to_file bu bilgisayar → yerel disk Yerel veritabanını .dump + .sha256 olarak yedekler

Yedekleme işleri salt okunurdur; geri yükleme işleri seçilen moda göre hedef veritabanını silip yeniden oluşturabilir.

Kurulum

git clone https://github.com/AzizARSLN/DBFerry.git
cd DBFerry
python -m pip install -r requirements.txt

Gereksinimler: Python 3.10+, paramiko, mcp ve PostgreSQL istemci araçları (pg_dump, pg_restore, psql). Windows'ta yerel işlemler için Git for Windows (Git Bash) gerekir.

Kullanım

python dbf.py web                      # yapılandırma ekranını aç (127.0.0.1:8787)
python dbf.py run --spec job.json      # planı göster, onay iste, çalıştır
python dbf.py inspect --spec job.json  # salt okunur ortam denetimi
python dbf.py profiles                 # kayıtlı sunucular
python dbf.py history                  # iş geçmişi
python dbf.py mcp                      # MCP sunucusu (stdio) — Claude, Cursor, Codex

Yapay zeka ajanları (MCP)

DBFerry aynı motoru bir Model Context Protocol sunucusu olarak da açar; ajanlar yedekleme, taşıma ve doğrulama işlerini plan → onay → çalıştır → doğrula akışıyla yürütür. Sunulan araçlar: db_profiles, db_inspect, db_plan, db_backup (salt okunur), db_restore (yıkıcı), db_verify, db_job_status.

Güvenlik: db_restore varsayılan olarak yalnızca plan döndürür (dry_run=false gerekir), replace modunda hedef veritabanı adının birebir onay belirteci olarak verilmesini ister, DBFERRY_READONLY=1 ayarlandığında sunucudan tamamen kaldırılır. Şifreler araç argümanı olarak alınmaz; bağlantı bilgileri yerel profillerden okunur.

Notlar

  • Her iş önce plan üretir; siz onaylamadan hiçbir şey değiştirilmez. Aynı liste çalışma sırasında ilerleme göstergesi olarak kullanılır.
  • Yedekler PostgreSQL özel formatında (pg_dump -Fc) alınır ve SHA-256 ile doğrulanır; her yedeğin yanına .sha256 dosyası yazılır. Boş local_copy_dir bırakılırsa dosyalar data/backups/<veritabanı>/ altına gider.
  • Profiller (data/profiles.json) şifreleri düz metin olarak tutar; bu dosyayı gizli veri gibi koruyun. Sürüm kontrolüne dahil edilmez ve tarayıcıya maskelenmiş gönderilir.
  • Web konsolunun kimlik doğrulaması yoktur ve varsayılan olarak 127.0.0.1 adresine bağlanır; dışarıya açmayın.
  • replace modunda hedef veritabanı silinip yeniden oluşturulur; güvenlik yedeğini kapatmadığınızdan emin olun.

Contributing

Bug reports and pull requests are welcome — please open an issue first for anything larger than a small fix.

Author

Aziz ARSLAN

About

Self-hosted PostgreSQL migration and backup tool with a local web console

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages