Developer's notes

Go to Blog

Today I needed to migrate a ~200 GB PostgreSQL database from one server to another. The source was PostgreSQL 17.10 running in a Docker container, and the destination was PostgreSQL 18.4, also in Docker.

There are several ways to move a PostgreSQL database between servers. I chose the simplest approach: create a dump with pg_dump and restore it with pg_restore.

I ran: docker exec -it postgres pg_dump -U postgres -Fc mydb > mydb.dump. This produced a 56 GB dump file. After transferring it to the destination server, I tried to restore it with: pg_restore -U postgres -d postgres -C mydb.dump. Instead of restoring the database, pg_restore crashed with a segmentation fault.

I investigated several possible causes:

After some debugging and a discussion with Claude, I discovered that the dump contained \r\n sequences. The problem was the -it option in docker exec, which had corrupted the binary output.

Allocating a TTY (-t) can:

You can detect this issue with: cat -v mydb.dump | head -20 | grep '\^M'.

The fix was simple: use -i instead of -it:

docker exec -i postgres pg_dump -U postgres -Fc mydb > mydb.dump

The new dump restored successfully.

Now I know the difference between docker exec -it and docker exec -i — and why TTY allocation and binary output don’t mix.