Connect two Docker containers having their own Docker Compose files
The provider container creates a Docker network that can be used by a consumer container.
I wanted to create a network between a database Docker container and an application Docker container, both having their own Docker Compose files. And I also wanted to make sure doing it right before implementing this in the actual docker-compose files. Test first baby ...
I also did this some time ago but had to look into this again because Docker Compose now has a name option in networks section. I am writing this post to document how I did it, hope you find it useful as well.
Two containers and commands for testing
In this setup I am using two containers:
- container#1: the database container
- container#2: the application container
A nice tool to play with containers and networking is the Busybox image. It's small and contains a lot of networking commands.
The database container (the provider) should contain a deamon that is listening to incoming connections. In the application container (the consumer) we run a program that calls the deamon in the database container.
For listening (an actual database deamon is listening) I first tried the obvious netcat command, for example when listening to port 1234:
nc -l -p 1234
But it appeared that netcat terminated when the connection got closed (by telnet) and this terminated the container. Netcat also has a '-k' persistent option but that did not work, it is a known problem. There appears to be a better version called ncat but that is not available in the Busybox image.
That's why I use the httpd command:
httpd -f -v -p 1234
Summary, in the database container we use httpd as the listening service.
Unfortunately curl is not available in the Busybox image and the wget command in Busybox does not support ports other than 80. That is why I use telnet in the application container to connect to the httpd server.
Finally, I use netstat to check the listening ports in the containers.
The two docker-compose files
The database docker-compose file, db.yml
In the database container we create the network 'postgres12_network'.
# db.yml version: '3.7' services: postgres12: image: busybox container_name: postgres12_container command: /bin/httpd -f -v -p 1234 networks: - default networks: default: name: postgres12_network
The application docker-compose file, app.yml
In the application container we consume the network 'postgres12_network'.
# app.yml version: '3.7' services: app: image: busybox container_name: app_container command: sleep infinity networks: - default - postgres networks: postgres: external: name: postgres12_network
Testing the database container
Be prepared, we use a lot of terminal windows here.
Open a terminal window, and start the database container:
docker-compose -f db.yml up
To check if the container is up, open another terminal window and type:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4ab28dc6d47b busybox "/bin/httpd -f -v -p…" 19 minutes ago Up 19 minutes postgres12_container
To check if the Docker network is created, type:
docker network ls
NETWORK ID NAME DRIVER SCOPE 539decc283dd postgres12_network bridge local
In a third terminal window we enter the database container, using exec, and start a shell (sh) session:
docker-compose -f db.yml exec postgres12 /bin/sh
Check if the httpd deamon is listening to port 1234:
netstat -l -t
Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 localhost:41825 0.0.0.0:* LISTEN tcp 0 0 :::1234 :::* LISTEN
Now connect to the httpd deamon:
telnet postgres12 1234
Connected to postgres12
In telnet, hit <ENTER> or type something else and hit <ENTER>.
HTTP/1.1 408 Request Timeout Date: Wed, 26 May 2021 11:42:22 GMT Connection: close Content-type: text/html <HTML><HEAD><TITLE>408 Request Timeout</TITLE></HEAD> <BODY><H1>408 Request Timeout</H1> No request appeared within 60 seconds </BODY></HTML> Connection closed by foreign host
It may look bad but it is in fact looking good, we got a response from the httpd deamon!
In the first terminal window there should be a message like:
postgres12_container | [::ffff:192.168.48.2]:33264: response:400
If you try telnet with a different port, the message will be:
telnet: can't connect to remote host (192.168.48.2): Connection refused
So far so good, let's move on to the application container.
Testing the application container
Open a terminal window, and start the application container:
docker-compose -f app.yml up
In another terminal window check if the container is up, type:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9df4c69b8772 busybox "sleep infinity" 58 seconds ago Up 57 seconds app_container 4ab28dc6d47b busybox "/bin/httpd -f -v -p…" 42 minutes ago Up 42 minutes postgres12_container
Both containers are up and running now.
In another terminal window we enter the application container, using exec, and start a shell (sh) session:
docker-compose -f app.yml exec app /bin/sh
Check if we can connect to the httpd deamon in the database container:
telnet postgres12 1234
Connected to postgres12
That's good, everything working as expected.
It is funny (NOT) that the question + answers on how to connect two Docker containers has a huge amount of votes on Stack Overflow, see links below. I think the documentation on the Docker website improved a lot but it needs more (trivial) examples. The 'name' option in the networks section makes it more easy to define networks and read what is going on in a docker-compose file.
Finally, Busybox is a small image but also very limited. It would be nice if more networking commands were added.
Links / credits
BusyBox - The Swiss Army Knife of Embedded Linux
Communication between multiple docker-compose projects
netcat - keep listening for connection in Debian
Networking in Compose
- Python Multiprocessing graceful shutdown in the proper order
- FastAPI + SQLAlchemy: Asynchronous IO and Back Pressure
- Connect two Docker containers having their own Docker Compose files
- Using Locust to load test a FastAPI app with concurrent users
- Documenting a Flask RESTful API with OpenAPI (Swagger) using APISpec
- Flask RESTful API request parameter validation with Marshmallow schemas
- Using UUIDs instead of Integer Autoincrement Primary Keys with SQLAlchemy and MariaDb
- Adding url_for() links to Jinja templates of a Flask multilanguage website
- Python Flask app on Docker in ISPConfig3 with Nginx - Part 1: Minimal app
- SQLAlchemy server-side datetime calculations
- Sending mail from a Docker container using ISPConfig3 host Postfix MTA
- Flask SQLAlchemy CRUD application with WTForms QuerySelectField and QuerySelectMultipleField