CLI tool
dimitri/pgloader avatar
dimitri/pgloader

pgloader v4: a Common Lisp loader that became a self-contained Clojure JAR

Migrate to PostgreSQL in a single command!

6,549 stars613 forksCommon LispNOASSERTION

At a glance

What is it?
The bulk loader for PostgreSQL explains its value in one sentence, keeps rejected rows instead of aborting the load, and has just been rewritten in Clojure on the JVM.
Who is it for?
pgloader earns its keep for one thing that `COPY` will not do for you: it keeps loading after a bad row and writes the rejects to a file instead of failing the whole table. The v4 rewrite makes it easier to run, since one JAR replaces an SBCL image and a native toolchain, but the README is candid that a Debian package is only planned and the release you can download today is a development build tagged v4-dev.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 39 days ago.
What is it written in?
Mainly Common Lisp, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • dimitri/pgloader6,549 ★ · +6
Star history
Repository2026-10-072026-10-23
dimitri/pgloader6.5k6.5k

6 Stars in the last 16 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The one behaviour that separates pgloader from COPY

The README opens with the argument rather than the feature list, and it is worth repeating exactly because it explains why the tool exists at all. PostgreSQL is transactional by default, so a single erroneous line in the input, whether that input is a file or a remote database, stops the entire bulk load for the table. If you are moving a million rows into a table with a not-null column that one of them violates, you find out at row 900,000 and start again.

pgloader's answer is a separate file of rejected data while it keeps copying the good rows. That is the whole thesis, and it also sets the limits: you are choosing a load that finishes with a known defect list over a load that either completes clean or rolls back. Neither is automatically right, but the reject file makes the first one auditable.

The README also positions the tool against Foreign Data Wrappers, and that comparison is fair. A foreign data wrapper gives you a live view of the source rather than a copy, which is a different thing and the better answer for ongoing replication. pgloader is a one-shot migration tool, and its `--on-error-stop` flag exists for the cases where you would rather have the transactional behaviour of raw `COPY` anyway.

The second transformation worth calling out is the one that quietly saves every MySQL import: MySQL datestamps like `0000-00-00` and `0000-00-00 00:00:00` become PostgreSQL `NULL`, because the PostgreSQL calendar has no year zero. That is data reformatting, not just copying, and it is the kind of detail that makes a migration tool earn its place.

Getting the v4 JAR onto a machine with Java 21

Version 4 is a full rewrite in Clojure distributed as a single self-contained JAR that needs Java 21 or later. The download URL is stable and always points at the most recent green build from the `v4-clojure-rewrite` branch, which is a useful trick and also a warning sign: there is no numbered v4 release to pin to.

bash
curl -L -o pgloader.jar \
  https://github.com/dimitri/pgloader/releases/download/v4-dev/pgloader.jar
java -jar pgloader.jar --version

To put it on the path system wide, the README installs the JAR into `/usr/local/lib` and writes a two-line shell wrapper to `/usr/local/bin/pgloader`:

bash
sudo install -m 755 pgloader.jar /usr/local/lib/pgloader.jar
echo '#!/bin/sh\nexec java -jar /usr/local/lib/pgloader.jar "$@"' \
  | sudo tee /usr/local/bin/pgloader
sudo chmod 755 /usr/local/bin/pgloader

Docker is the second route, and the CI image is built on every push to the rewrite branch:

bash
docker pull ghcr.io/dimitri/pgloader:latest
docker run --rm -it ghcr.io/dimitri/pgloader:latest pgloader --version

The Debian and Ubuntu route is v3 only. The README says plainly that `apt-get install pgloader` gets you the v3 package and that a v4 Debian package is planned, which means distribution packaging is the one place where the rewrite has not landed yet.

What the Clojure rewrite actually changed

v4 is described as a drop-in replacement for v3 that accepts the same `.load` file syntax and the same command-line flags, which is the important promise for anyone with existing load files. What changed underneath is substantial enough to list.

The single JAR removes SBCL, native dependencies and shared libraries, so an install is now a download and a `java -jar`. Connection strings became JDBC URLs, meaning `jdbc:mysql://`, `jdbc:postgresql://` and `jdbc:sqlserver://` everywhere, with driver-specific parameters passing through unchanged. SSL configuration moved into the URI, so `?sslmode=require` and `?sslmode=disable` replace the old `--no-ssl-cert-verification` flag, and that is a genuine improvement rather than a rename because the setting is now visible in the connection string itself.

The fourth change is the one that matters most on large migrations. The old Common Lisp implementation ran into heap exhaustion on big loads, and v4 moves that to the Java heap where `-Xmx` is a flag everybody already knows. Heap exhaustion is not a bug you can patch around from the outside; being able to hand it to the JVM's own memory management is the practical upgrade.

The source tree still shows the Lisp lineage in plain sight. There is a `clojure/` directory, a `src/` tree, `pgloader.asd` as the ASDF system definition, `Dockerfile.ccl` for the Clozure-CL build, and a Makefile that still declares `VERSION = 3.6.10` and drives the CCL and SBCL build paths. The v3 machinery has not been deleted, which is why both the Lisp and Clojure trees coexist.

Running a first migration with a dry run first

The usage is either a command file or everything on the command line, and the README points at the quick start in the Read the Docs tutorial for the details. The shape is source then target:

bash
java -jar pgloader.jar <file.load>
java -jar pgloader.jar ./test/sqlite/sqlite.db postgresql:///newdb

The SQLite example is the smallest complete case: create a database, then hand the tool a file and a target URL. The MySQL example is the full one, and the README notes that it migrates schema definition too, covering tables, indexes, foreign keys and comments, plus parallel loading of the corrected data:

bash
createdb pagila
java -jar pgloader.jar mysql://user@localhost/sakila postgresql:///pagila

Before either, run `--dry-run`, which the usage output describes as checking connections only and loading nothing. On a migration of any size that is the difference between finding out you mistyped a connection string in ten seconds and finding out after the first million rows.

The option list in the README is short and mostly about logging and stopping behaviour: `--verbose`, `--debug`, `--logfile`, `--summary`, `--client-min-messages`, `--root-dir` defaulting to `/tmp/pgloader/`, `--list-encodings`, and the `--on-error-stop` discussed above. The `--root-dir` default is worth noting because it is where the reject file lands, and knowing that path before you start is easier than finding it afterwards.

Release history and where the project actually stands

The release list tells a clear story about a project mid-transition. The v4-dev entry, published on 2026-06-20, is an automated build from `main` at a specific commit and reports that all CI tests passed, with a build string of `4.0.0-231ab86`. Underneath it, the last tagged release is v3.6.9 from 2022-10-24, followed by v3.6.8 from 2022-09-26.

So the gap between the last numbered release and the current development build is about four years, and the v3 changelog entries show what that era was doing: default support for MSSQL auto-incrementing bigint and smallint, an upgrade of Clozure-CL in `Dockerfile.ccl`, sequences with a `minvalue` defined, and a fix for mapping MySQL signed int with auto-increment to PostgreSQL `serial`. The last item on that list is the kind of mapping bug that takes a real migration to find.

The repository is not archived and the last push was on 2026-09-14, so the project is moving, and the Read the Docs badge in the README tracks the documentation build. Licence is The PostgreSQL Licence, which is permissive and close enough to the BSD family that the copyleft question does not arise for most users.

`AGENT.md` and `TODO.md` both sit in the tree, alongside a `Vagrantfile` and bootstrap scripts for several Linux distributions, which is the residue of a project that used to support installing itself from source on many platforms. The rewrite trades that matrix for a JAR, and the docs now live at pgloader.readthedocs.io rather than in the repository, so the README is a summary with pointers rather than a manual.

Where pgloader is the wrong choice

The most common mistake with pgloader is treating it as a continuous replication tool. It is a bulk loader. If the requirement is a live view of a MySQL database inside PostgreSQL, a Foreign Data Wrapper does that job and pgloader's own README says so, and if the requirement is ongoing change capture, neither tool is what you want.

The second mistake is trusting a load with the reject file unread. The rejected rows are written to a file, not surfaced loudly, so a migration that reports success can still have dropped data, and the failure mode looks like success from a distance. The `--summary` flag exists for exactly this reason, and reading both it and the rejects is the difference between a migration you can sign off and one you are hoping about.

A third constraint is the v4 packaging state. Anyone on a managed platform that blocks writing to `/usr/local/bin` or running arbitrary JARs will have more friction than the README suggests, since the official install path writes a wrapper there with `sudo`. The Docker route avoids that, and the `apt` route avoids it entirely but installs v3 instead.

On Windows the README is silent, and the related search terms people use include Windows install and Windows binary, which suggests it is a common question the documentation does not answer. Given that the v3 build was Common Lisp with native components and v4 is a JAR, the JVM route is the more likely to work there, but the README does not claim it and neither should anyone else.

Editorial conclusion

pgloader earns its keep for one thing that `COPY` will not do for you: it keeps loading after a bad row and writes the rejects to a file instead of failing the whole table. The v4 rewrite makes it easier to run, since one JAR replaces an SBCL image and a native toolchain, but the README is candid that a Debian package is only planned and the release you can download today is a development build tagged v4-dev. Start with `java -jar pgloader.jar --dry-run` against a copy of your data, read the rejected rows file before trusting the load, and keep the v3.6.9 package from your distribution in mind if a stable release matters more than the single-file install.

Frequently asked questions

How can I use Pgloader to migrate data from SQL Server to PostgreSQL?

Point the JDBC connection string at the source and a PostgreSQL URL at the target, for example `java -jar pgloader.jar jdbc:sqlserver://<host>:<port>;databaseName=<db> postgresql:///targetdb`. The v4 rewrite introduced `jdbc:` prefixes and passes driver-specific parameters through unchanged, and the v3.6.9 release added default handling for MSSQL auto-incrementing bigint and smallint.

Is there a free IDE for PostgreSQL?

pgloader is not a database IDE, it is a one-shot bulk loader that uses the PostgreSQL `COPY` command. It reads from files or remote databases and writes into a target PostgreSQL database, keeping rejected rows in a separate file instead of aborting the load on the first bad line.

How do I install pgloader without a native Lisp toolchain?

Use the v4 JAR, which needs Java 21 or later and has no native dependencies. Either download `pgloader.jar` from the v4-dev release and run it with `java -jar`, or pull `ghcr.io/dimitri/pgloader:latest` and run the image. The Debian and Ubuntu package is still v3 only, and a v4 package is planned.

Official sources

  1. dimitri/pgloader on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/dimitri-pgloader.svg)](https://hysenlabs.com/projects/dimitri-pgloader)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/dimitri-pgloader.svg)](https://hysenlabs.com/projects/dimitri-pgloader)

Was this useful?