Looking for a modern alternative? Check out pgferry — our easier and more robust replacement for pgloader.
A fork of dimitri/pgloader with MySQL 8.4+ compatibility fixes, performance optimizations, and a heap size fix for large migrations.
What's different from upstream:
- MySQL 8.4+ / 9.0+ support — Fixes
caching_sha2_passwordauthentication so pgloader works with modern MySQL servers - Performance optimizations — Automatic bulk-load GUCs, parallel index workers, adaptive batch sizing, MySQL read tuning, and opt-in UNLOGGED table loading
- Heap size fix — The Docker image actually uses the configured 16 GB heap instead of SBCL's default 1 GB, preventing heap exhaustion on large migrations
docker pull ghcr.io/limetric/pgloader:latestMigrate a MySQL database to PostgreSQL:
docker run --rm --network host ghcr.io/limetric/pgloader:latest \
pgloader mysql://user:pass@localhost/sourcedb \
pgsql://user:pass@localhost/targetdbIf your databases are in Docker, use service names instead of localhost and connect via a shared network:
docker run --rm --network my-network ghcr.io/limetric/pgloader:latest \
pgloader mysql://root:pass@mysql-host/mydb \
pgsql://postgres:pass@pg-host/mydbMigrate a SQLite file (mount it into the container):
docker run --rm -v /path/to/data:/data ghcr.io/limetric/pgloader:latest \
pgloader /data/source.db pgsql://user:pass@localhost/targetdbUse a .load command file for advanced options (cast rules, schema renaming, filtering):
docker run --rm --network host -v /path/to/commands:/commands ghcr.io/limetric/pgloader:latest \
pgloader /commands/migration.loadUpstream pgloader fails to connect to MySQL 8.4+ because these versions use caching_sha2_password as the default authentication method. The underlying MySQL protocol library (qmynd) has two issues:
-
Auth-switch packet type mismatch — The
auth-switch-requestpacket defines the auth plugin data as a string instead of raw octets. When the server sends an auth-switch (e.g., amysql_native_passworduser on a MySQL 8.4+ server), the binary scramble is incorrectly converted to a Lisp string, causing a type error in the cryptographic functions. This fork patches qmynd at build time to fix this. -
Missing RSA dependencies — Full
caching_sha2_passwordauthentication over TCP requires RSA encryption. qmynd declares the needed packages (asn1,trivia) as optional, so they aren't downloaded automatically. This fork adds them as explicit dependencies.
Files changed:
pgloader.asd— Added#:asn1and#:triviadependenciesMakefile— Patches qmynd'sauth-switch-requestpacket to use(octets :eof)instead of(string :eof)after cloningDockerfile— Updated base image to Debian Trixie
Upstream's make save target (used by the Dockerfile) does not pass --dynamic-space-size to SBCL, so the saved image always gets SBCL's default 1 GB heap regardless of the DYNSIZE variable. This causes heap exhaustion on large migrations. This fork fixes the save target to respect DYNSIZE (default 16 GB).
The following optimizations are applied automatically during database migrations:
- Bulk-load session GUCs — Automatically sets
synchronous_commit=off,maintenance_work_mem=512MB, andwork_mem=64MBon PostgreSQL writer connections for improved throughput. User-supplied values viaSETin.loadfiles are never overridden. Skipped for Redshift targets. - Parallel index workers — Sets
max_parallel_maintenance_workers=2on each index creation connection (PostgreSQL 11+), allowing individual CREATE INDEX operations to use parallel workers alongside the existing cross-table parallel index building. - MySQL read tuning — Sets
REPEATABLE READisolation level andnet_write_timeout=600on MySQL reader connections for consistent reads and tolerance of slow networks during large migrations. - Adaptive batch sizing — After the first batch per table, computes average row size and adjusts subsequent batches to target ~50 MB (up to 100k rows). Reduces round-trip overhead for tables with small rows.
The following optimizations are available as opt-in .load file options:
- UNLOGGED tables —
WITH unloggedcreates target tables as UNLOGGED during the load (no WAL writes), then converts them back to LOGGED after data and indexes are complete. Provides 2-3x write throughput improvement. Anunwind-protectcleanup ensures tables are re-logged even if the load is interrupted. Only safe for fresh migrations where crash-time data loss is acceptable.
make clean && make
./build/bin/pgloader --versionOr via Docker:
docker build -t pgloader .Full pgloader documentation is available at pgloader.readthedocs.io, including the command file syntax, cast rules, and supported source formats (MySQL, SQLite, MS SQL Server, CSV, fixed-width, DBF, IXF).
pgloader is available under The PostgreSQL Licence.