I’m a proponent of using Docker containers locally when running 3rd party software except when it’s PostgreSQL. Here are a couple of reasons why would you prefer local PostgreSQL over containerized PostgreSQL:
- Multiple projects. Run personal, work-related, or any other container that needs DB access.
- Persistence. Managing persistent storage for databases in Docker containers can be cumbersome. Ensuring data is safely stored and backed up requires additional configuration for volumes and bind mounts. Running PostgreSQL locally simplifies persistence management since the data resides directly on the host filesystem.
- Ease of Access. Accessing PostgreSQL directly on the host is generally more straightforward. Tools and applications can connect to the database using the local hostname without needing to manage Docker’s networking setup.
- Configuration and Customization. Configuring PostgreSQL natively allows for more flexibility and easier customization. While you can pass configuration files to a Docker container, making changes to a local installation can be more intuitive, immediate, and support your learning for PostgreSQL internals.
- Learning and Troubleshooting. For those learning PostgreSQL or troubleshooting issues, running the database locally can be more beneficial. It allows for direct interaction with PostgreSQL’s logs, configurations, and utilities without the added layer of Docker, making debugging and learning processes more transparent.
By preferring a local installation of PostgreSQL, you leverage the benefits of direct integration with the host system, improved performance, and simplified management, all of which can enhance development and operational workflows.
So, how do you share your local PostgreSQL server running on @localhost:5432
with any Docker container? Let’s see two
different methods by using networking: Docker Host and Bridge network.
Table of contents
Open Table of contents
Setup
For this experiment, we need the following:
- PostgreSQL 15.7 up and running in your machine
- Verify installation with
psql postgres -c 'SELECT version();'
.
- Verify installation with
- Docker 26.1.1
- Verify installation with
docker run --rm hello-world
.
- Verify installation with
Docker Host Network
As the name implies, when using the host
option, the containers will not run in their network stack. Instead, the
containers will share the host’s network and IP address.
You can set up the container’s PostgreSQL URL as you would normally do when setting up a PostgreSQL client (ORMs,
libraries, etc) in a development environment, i.e. postgres://postgres@localhost:5432/db_name
.
Let’s test this!
Note:
- If you are in Linux, the host network feature is available without any changes.
- If you are in MacOS, follow these instructions to enable the host networking feature in Docker Desktop.
Run:
docker run --rm -it --network=host alpine sh
# Inside the container run
apk add postgresql-client
psql -h localhost -U postgres postgres;
# Inside psql. You should be able to list all the databases from your local PostgreSQL server
\list
# Hit Ctrl+D to exit.
Now you can run any docker container and connect it to your running local PostgreSQL server.
Docker Bridge Network
The Bridge Network is the default network driver used by containers
when the --network
option is not specified when running containers. The bridge network, as the name implies, is a
bridge that enables containers to communicate within the same network but isolated from the host’s network.
To enable communication from any container in the default bridge network, we need to:
- Add localhost host into container’s
/etc/hosts
by using docker run--add-host=host.docker.internal:host-gateway
option. Docs here. - Modify local
postgresql.conf
to allow incoming connections from the container’s bridge network. - Modify local
pg_hba.conf
to trust client connections from the container’s bridge network.
First, let’s test the network communication from the container to a host server. Run two terminals:
- Terminal 1
- Install http-server:
npm install -g http-server
- Run local http-server
http-server
- Install http-server:
- Terminal 2
- Run container
docker run --rm -it --add-host=host.docker.internal:host-gateway alpine sh
. Inside the container:- Install CURL
apk add curl
- Query host’s http-server
curl --silent --output null http://host.docker.internal:8080 -v
- Install CURL
- Run container
In the first terminal, you should see a connection from the terminal 2.
In the second terminal, you should see Host: host.docker.internal:8080
and the response HTTP/1.1 200 OK
.
Now that we’ve verified the container can connect to localhost through the bridge network, let’s configure our local PostgreSQL server to allow connections from the same bridge network.
Docker Bridge Network Gateway and Subnet
To allow incoming connections from the Docker bridge network into PostgreSQL, we first need to know the network’s gateway and subnet.
docker network inspect bridge | jq -r '.[] .IPAM.Config[].Gateway'
docker network inspect bridge | jq -r '.[] .IPAM.Config[].Subnet'
The default bridge network gateway is: 172.17.0.1
, and the default network subnet is: 172.17.0.0/16
.
Configure local PostgreSQL server
Depending on your OS, the PostgreSQL config files can be in different directories. The easiest way to identify the files
is by running the following commands in psql postgres
locally.
postgresql.conf
SHOW config_file;
Copy the file path, edit postgresql.conf
, uncomment list_addresses
, and add the Bridge Network Gateway.
# - Connection Settings -
# Allow connections from Docker Bridge Network Gateway
listen_addresses = 'localhost, 172.0.0.1'
pg_hba.conf
SHOW hba_file;
Copy the file path, edit pg_hba.conf
, add this to the bottom of the file with the Docker Bridge Network Subnet.
# Allow trusted connections from Docker Bridge Network Subnet
host all all 172.17.0.0/16 trust
Run container and connect to local PostgreSQL Server
Now let’s verify that a container can connect to our local PostgreSQL server.
Run:
docker run --rm -it --add-host=host.docker.internal:host-gateway alpine sh
# Inside the container run
apk add postgresql-client
# We use the 'host.docker.internal' host
psql -h host.docker.internal -U postgres postgres;
# Inside psql. You should be able to list all the databases from your local PostgreSQL server
\list
# Hit Ctrl+D to exit.
You should be able to see the internal docker host (specified in the --add-host
option) in /etc/hosts
by running cat /etc/hosts
.
As you would have guessed, you need to use the host.docker.internal
host to set up the PostgreSQL connection in your
ORM, library, or postgres://
DB URL.
What method should you choose?
As always, it depends.
- If you want to have the easiest setup to share your local PostgreSQL, choose Host Network.
- If you want to decide how you share your local PostgreSQL, choose Bridge Network.
I recommend the Bridge Network, here are my reasons:
- Bind the container’s ports to your choosing. When you use Host Network, you can’t publish different port mappings from the ports the container exposes.
- Isolate the container’s network access. Share localhost access per container. I don’t want any container to have access to my localhost. If you have NAS or any other localhost services you would be giving access to the containers.
Containers are here to stay. It’s best to learn networking 101 (see the links), and learn Docker’s network drivers.
Useful links
Here are some links that helped me understand what I wrote in this post.
- Docker
- Networking
- PostgreSQL
Do you need help setting it up? Let me know in the comments or send me an email at [email protected] 😃.
Thank you @ethanppl for reviewing this post.