Networking With Docker Compose (Quick Guide)

published
October 29, 2024
TABLE OF CONTENTS
Build Your Dream Network Architecture
Sign up for a 2-week free trial and experience seamless remote access for easy setup and full control with Netmaker.

Docker Compose is a scDocker Compose is a script that tells the Docker container operating system how to spin up and connect different services. With Docker Compose, you don't have to start each container individually. Instead, you can use a single YAML file to describe how your environment should look.

For instance, say you have a web application with a front end, a back end, and a database. Normally, you would need to start each of these containers with various parameters. 

But using Docker compose, you can define all the services in a `docker-compose.yml` file. This file specifies everything, including network configurations and volumes. So, you can bring up your entire application with a single command: `docker-compose up`.

How networking works in Docker Compose

By default, Docker Compose creates a single network for your application. All the services defined in the Compose file are connected to this default network, so they can easily communicate with each other. 

For example, if your web service needs to talk to the database service, it can do so just by referencing the service name as the hostname.

Sometimes you need more control over the networking. Docker Compose allows you to define custom networks in the YAML file. This is helpful when you want to isolate different parts of our application. 

For instance, you might want a separate network for front-end services and another for back-end services. You can define these networks and assign services to them as needed.

Let’s illustrate with a quick example of a `docker-compose.yml`.

version: '3'
services:
  Web:
    image: my-web-app
    ports:
      - "80:80"
    networks:
      - front-end
  api:
    image: my-api
    networks:
      - back-end
      - front-end
  database:
    image: my-db
    networks:
      - back-end

networks:
  front-end:
  back-end:

In this setup, we have three services: `web`, `api`, and `database`. The `web` service is connected to the `front-end` network, while the `database` is on the `back-end` network. 

The `api` service is on both, so it can communicate with both the `web` and `database` services. This way, you can control the flow of traffic and isolate different segments of your application as needed.

Using Docker Compose networks simplifies the complexity of connecting various services. It abstracts away a lot of the manual networking setup, letting you focus on building and running your applications.

Want enterprise-grade security for your Docker container networks? Try Netmaker's secure networking platform and see how easy it can be to manage container networking at scale.

Setting up Docker Compose networks

Setting up Docker Compose networks involves just a few steps. First, you define the networks in the `docker-compose.yml` file. Then, you assign your services to these networks. This lets you control how different parts of your application talk to each other.

Suppose you need to set up a web application with a front end, an API, and a database. You want the web and API services to share one network, while the API and database share another. 

Here's how you do it:

version: '3'
services:
  web:
    image: my-web-app
    ports:
      - "80:80"
    networks:
      - front-end
  api:
    image: my-api
    networks:
      - front-end
      - back-end
  database:
    image: my-db
    networks:
      - back-end

networks:
  front-end:
  back-end:

In this setup, you have three services: `web`, `api`, and `database`. First, you define these services under the `services` section. For each service, you specify the Docker image they should use. For example, your web service uses the `my-web-app` image.

Next, you map the ports for the `web` service, exposing port 80 on your host to port 80 inside the container. This way, you can access your web service from a browser.

Then, you assign networks to each service. The `web` service is connected to the `front-end` network, which it will share with the `api` service. 

The `api` service also connects to the `back-end` network, allowing it to talk to the `database` service. The `database` service is only on the `back-end` network, isolating it from the `web` service.

Finally, you define the `front-end` and `back-end` networks under the `networks` section. You don't need to provide much detail here; just naming the networks is enough for Docker Compose to create them automatically.

This setup lets the `web` and `api` services communicate via the `front-end` network. The `api` and `database` services talk through the `back-end` network. The `web` service can't directly access the `database`, adding a layer of security.

Once everything is set up in the `docker-compose.yml` file, you bring up your application with `docker-compose up`. Docker Compose reads the file, creates the necessary networks, and starts the services. They can now communicate as specified. This way, you ensure my services are isolated and can only communicate where necessary.

Bridge network

In Docker, the bridge network is the default network type. When you don’t specify a custom network in your Docker Compose file, Docker automatically connects your containers to the default bridge network. This makes sure they can communicate with each other.

Let’s say you have a simple application with two services: a web server and a database. If you don't define any networks in your `docker-compose.yml`, Docker uses the default bridge network. Here’s what that file might look like:

version: '3'
services:
  web:
    image: my-web-app
    ports:
      - "80:80"
  database:
    image: my-db

In this version, you are explicitly creating a bridge network called `my-bridge-network`. Both the web and database services are connected to this custom network. To define this network, you use the `driver` option set to `bridge`.

Using a custom bridge network gives me flexibility. For example, you can define network-specific options like subnet and gateway. Here’s an advanced example with custom settings:

version: '3'
services:
  web:
    image: my-web-app
    ports:
      - "80:80"
    networks:
      - custom-bridge
  database:
    image: my-db
    networks:
      - custom-bridge

networks:
  custom-bridge:
    driver: bridge
    ipam:
      config:
        - subnet: "192.168.1.0/24"
          gateway: "192.168.1.1"

In this setup, you define a bridge network named `custom-bridge` with specific IPAM (IP Address Management) settings. You will specify a subnet and a gateway, giving you more control over the network’s IP range. This can be useful in larger applications where you need to avoid IP conflicts or when you want to make your network settings more predictable.

By using bridge networks, whether the default or custom ones, you ensure your services can easily communicate within a confined environment. This abstraction makes networking simpler and more effective, letting you focus on the core logic of your application.

Host network

In Docker, the host network is another option you can use for connecting my containers. Unlike the bridge network, the host network mode allows your container to share the same network stack as the host machine. This means that the container's network interface is directly connected to the host’s network.

When you use the host network, there’s no isolation between the host and the container. This can be useful in scenarios where you need the highest network performance, such as running a containerized service that requires low latency or high throughput.

Setting up a host network in Docker Compose is straightforward. You just need to specify `network_mode: "host"` for the services you want to connect directly to the host’s network. Here’s an example with a web server service:

version: '3'
services:
  web:
    image: my-web-app
    network_mode: "host"

Here, the `web` service uses the host’s network stack. This means it will access the network just as if it were running directly on the host machine. 

For instance, if your host machine’s IP address is `192.168.1.100`, your web service will also be accessible at `192.168.1.100`.

Using the host network can sometimes simplify configurations. For example, you don’t need to map ports between the host and the container. All the ports exposed by your container are accessible on the host directly. This can be handy when dealing with services like DNS, DHCP, or other network-heavy applications.

However, there are some limitations. Host network mode only works on Linux hosts. Also, it eliminates the network isolation provided by Docker, which can be a security concern. All the services share the same network namespace, which means they can interfere with each other more easily.

Here’s another example where you have a database service using the host network:

version: '3'
services:
  database:
    image: my-db
    network_mode: "host"

In this case, your `database` service connects directly to the host’s network stack. If your database listens on port `5432`, you can connect to it using the host’s IP and port `5432`.

Mixing different network modes in a Docker Compose setup can lead to complex configurations. For most scenarios, it’s best to use bridge networks for their isolation and flexibility. But in certain cases where performance is critical or the service needs direct access to the host’s network, the host network mode is a powerful option.

By using the host network mode, you gain direct network access and high performance at the cost of isolation and portability. This trade-off might be acceptable for specific use cases, making it a valuable tool in your Docker Compose toolkit.

Overlay network

An overlay network in Docker is a powerful feature that allows containers running on different Docker hosts to communicate securely. It's particularly useful in a distributed or clustered environment where services need to talk to each other across multiple machines. With overlay networks, Docker takes care of the complicated network setup, so I can focus on my application’s logic.

Setting up an overlay network in Docker Compose involves a few additional steps compared to a bridge or host network. First, you need to make sure you are using Docker Swarm or another orchestrator that supports multi-host networking. Swarm mode makes it easier to manage nodes and services across a cluster.

Once your swarm is set up, you define the overlay network in your `docker-compose.yml` file just like any other network. Here’s an example:

version: '3'
services:
  web:
    image: my-web-app
    ports:
      - "80:80"
    networks:
      - my-overlay-network
  database:
    image: my-db
    networks:
      - my-overlay-network

networks:
  my-overlay-network:
    driver: overlay

In this setup, you specify a network called `my-overlay-network` with the `overlay` driver. Both the `web` and `database` services are connected to this network. This configuration allows these services to communicate, even if they’re running on different hosts in my Swarm cluster.

One of the great things about overlay networks is their built-in encryption. Docker secures the network traffic between nodes, which is crucial for data privacy and compliance. You don't need to set up a VPN or other complicated security measures; Docker handles it out of the box.

Here’s another example where you have three services: a web front end, an API, and a backend database. This time, you will define two overlay networks to isolate the front end from the backend:

version: '3'
services:
  web:
    image: my-web-app
    ports:
      - "80:80"
    networks:
      - front-end-net
  api:
    image: my-api
    networks:
      - front-end-net
      - back-end-net
  database:
    image: my-db
    networks:
      - back-end-net

networks:
  front-end-net:
    driver: overlay
  back-end-net:
    driver: overlay

In this configuration, the `web` and `api` services share the `front-end-net` overlay network, while the `api` and `database` services share the `back-end-net` overlay network. 

The `api` service acts as a bridge between the two networks. This design isolates the web service from directly accessing the database, adding an extra layer of security and organization.

To deploy this setup in a Docker Swarm, you simply run `docker stack deploy -c docker-compose.yml my-stack`. Docker Swarm takes care of creating the networks across all nodes and deploying my services. They can now communicate as defined, regardless of which host they’re running on.

Overlay networks offer flexibility and security, bridging the gap between containers on different hosts. They’re a key feature when you are working with distributed systems, allowing you to build scalable and secure applications without getting bogged down by complex network configurations.

None network

In Docker, the "none" network mode is a unique option that effectively isolates a container from any network. When you use the "none" network mode, the container has no access to external networks, including the host’s network and other containers. It’s like putting the container in a bubble, making it completely isolated in terms of networking.

Setting up a "none" network in Docker Compose is quite simple. You just need to specify `network_mode: "none"` for the services you want to isolate. Here’s an example with a simple service that doesn’t need any network access:

version: '3'
services:
  my-service:
    image: my-isolated-app
    network_mode: "none"

In this setup, the `my-service` container will have no network interfaces apart from the loopback interface. It won’t be able to communicate with other containers or external networks. This can be useful for services that don’t need network access, like a data processing task that reads from and writes to a mounted volume.

There are scenarios where isolating a container with the "none" network mode makes sense. For instance, if you have a batch processing job that just needs to run some calculations and save the results to a file, you might not want it to have network access. This reduces the attack surface and ensures the job runs in a secure, isolated environment.

Here’s another example where you have multiple services, but one of them needs to be isolated from the network:

version: '3'
services:
  web:
    image: my-web-app
    ports:
      - "80:80"
    networks:
      - my-network
  database:
    image: my-db
    networks:
      - my-network
  isolated-task:
    image: my-task
    network_mode: "none"

networks:
  my-network:
    driver: bridge

In this configuration, the `web` and `database` services are connected to the `my-network` bridge network, allowing them to communicate. However, the `isolated-task` service uses the "none" network mode, isolating it completely. 

This setup is useful if the `isolated-task` service needs to process data without any network dependencies or risks of network-related interruptions.

Using the "none" network mode is a straightforward way to enforce strict isolation. It ensures that the container has no network access, which can be beneficial for certain workloads. 

Whether you are running isolated tasks or services with strict security requirements, the "none" network mode provides an easy solution to achieve complete network isolation. 

By leveraging this mode, you can focus on the specific tasks your container needs to perform without worrying about unintended network interactions.

Custom networks

Creating custom networks in Docker Compose gives you more control over how your containers communicate, isolating specific parts of your application as needed. By defining custom networks, you can ensure that only the necessary services can access each other, enhancing security and organization.

To set up a custom network in Docker Compose, you start by defining the networks in your `docker-compose.yml` file. Here's a simple example where you have a web application and a database:

version: '3'
services:
  web:
    image: my-web-app
    ports:
      - "80:80"
    networks:
      - custom-network
  database:
    image: my-db
    networks:
      - custom-network

networks:
  custom-network:
    driver: bridge

In this setup, we have created a custom bridge network called `custom-network`. Both the `web` and `database` services are connected to this network. 

By defining a custom network, you will have the flexibility to configure network-specific options such as subnets and gateways, making your network setup more predictable and tailored to my needs.

Sometimes, you need to isolate different parts of your application for security or organizational purposes. For example, you might want to separate the front-end and back end services into different networks. Here's how you can do it:

version: '3'
services:
  web:
    image: my-web-app
    ports:
      - "80:80"
    networks:
      - front-end-network
  api:
    image: my-api
    networks:
      - front-end-network
      - back-end-network
  database:
    image: my-db
    networks:
      - back-end-network

networks:
  front-end-network:
    driver: bridge
  back-end-network:
    driver: bridge

In this example, you have two custom networks: `front-end-network` and `back-end-network`. The `web` and `api` services share the `front-end-network`, while the `api` and `database` services share the `back-end-network`. 

The `api` service acts as a bridge between the front end and back-end, allowing controlled communication. This setup isolates the `web` service from the `database`, adding an extra layer of security.

Custom networks also come in handy when working with more complex network topologies. Suppose you need to define specific IP ranges for different networks. Here's how you can configure that:

version: '3'
services:
  web:
    image: my-web-app
    ports:
      - "80:80"
    networks:
      - custom-bridge
  api:
    image: my-api
    networks:
      - custom-bridge
  database:
    image: my-db
    networks:
      - custom-bridge

networks:
  custom-bridge:
    driver: bridge
    ipam:
      config:
        - subnet: "192.168.2.0/24"
          gateway: "192.168.2.1"

In this configuration, you define a custom bridge network named `custom-bridge` with specific IPAM settings. You will set a subnet and gateway to control the IP range used by this network. This approach is useful for larger applications where you need to avoid IP conflicts or ensure specific network policies.

By creating custom networks, you can tailor the network setup to my application's needs. This flexibility allows you to isolate services, control traffic flow, and enhance security. 

Docker Compose makes it straightforward to define and manage these custom networks, abstracting away much of the complexity and letting you focus on building and running my applications.

Best practices for using Docker Compose

Running Docker across multiple clouds or data centers? Netmaker's overlay networking platform creates a unified, secure network fabric across any infrastructure. Get started today and simplify your multi-cloud networking.

Always define custom networks

Custom networks provide better control over how containers communicate. For instance, if you have a microservices architecture, you usually create separate networks for different components. This isolation helps limit the communication only to necessary services.

For example, in a typical setup, you might have a `front-end-network` and a `back-end-network`. The front end and API services are on the `front-end-network`, while the API and database services share the `back-end-network`:

version: '3'
services:
  web:
    image: my-web-app
    ports:
      - "80:80"
    networks:
      - front-end-network
  api:
    image: my-api
    networks:
      - front-end-network
      - back-end-network
  database:
    image: my-db
    networks:
      - back-end-network

networks:
  front-end-network:
    driver: bridge
  back-end-network:
    driver: bridge

This setup ensures that the web service can’t directly access the database, adding an extra layer of security.

Use meaningful network names

Naming networks clearly can help when debugging or scaling services. Names like `frontend`, `backend`, or `database-layer` make it obvious what each network is for, which is crucial for team collaboration.

When defining networks, you often configure IP address management (IPAM) settings to avoid conflicts and control IP ranges. 

It’s particularly helpful in larger setups where you might have multiple applications or environments running. Here’s an example of how you configure a custom bridge network with specific IP ranges:

networks:
  custom-bridge:
    driver: bridge
    ipam:
      config:
        - subnet: "192.168.2.0/24"
          gateway: "192.168.2.1"

By setting a specific subnet and gateway, you make the network configuration predictable and easier to manage.

For better performance in specific scenarios, you may sometimes use the host network mode. For instance, when you need low latency or high throughput, like in a real-time analytics service, you opt for the host network. However, this has limitations and security implications, so use it judiciously. 

Here’s a quick example:

version: '3'
services:
  analytics:
    image: my-analytics-app
    network_mode: "host"

Keep containers isolated when network access isn’t needed

For services that don’t require any network interaction, using the `none` network mode is a best practice. This approach enhances security by isolating the container. 

Here’s how you might set up an isolated batch processing service:

version: '3'
services:
  batch-job:
    image: my-batch-app
    network_mode: "none"

This configuration ensures the batch job runs without any network access, reducing the risk of external interference.

Always keep security at the forefront

For multi-host setups, you must leverage Docker’s overlay networks, which provide built-in encryption and secure communication across nodes. 

Here’s an example with an overlay network:

version: '3'
services:
  web:
    image: my-web-app
    ports:
      - "80:80"
    networks:
      - overlay-net
  database:
    image: my-db
    networks:
      - overlay-net

networks:
  overlay-net:
    driver: overlay

By using overlay networks, you ensure my services can securely communicate across different hosts, which is crucial for distributed applications.

Following these best practices helps you build robust, secure, and scalable network configurations for your Docker Compose applications, making it easier to manage and maintain them in the long run.

Secure Docker Container Access with Netmaker

While Docker Compose's built-in networking capabilities are powerful for local development and single-host deployments, modern applications often require secure remote access and multi-host networking. Netmaker provides an additional layer of security and flexibility for Docker networks, especially when dealing with remote access and cross-host communication.

Why Use Netmaker with Docker?

Docker's default networking works well for basic scenarios, but when you need:

  • Secure remote access to containers
  • Cross-host container communication
  • Encrypted traffic between services
  • Fine-grained access control
  • Multi-cloud container networking

Netmaker fills these gaps by creating secure, encrypted networks that integrate seamlessly with Docker containers.

Setting Up Netmaker with Docker Compose:

Before implementing this setup, you'll need access to a Netmaker server. You have two options:

  • Netmaker SaaS: Sign up for a managed Netmaker instance
  • Self-Hosted: Deploy Netmaker on your infrastructure using our guide.

Here's how to integrate Netmaker with your Docker Compose environment:

services:
  netclient:
    image: gravitl/netclient:latest
    container_name: netmaker_gateway
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    environment:
      - TOKEN=your_enrollment_token_here    # Replace with your actual token
    volumes:
      - /dev/net/tun:/dev/net/tun
    networks:
      netmaker_network:
        ipv4_address: 172.20.0.10
    restart: unless-stopped

  database:
    image: postgres:15-alpine
    container_name: demo_db
    environment:
      - POSTGRES_PASSWORD=demopass
      - POSTGRES_USER=demouser
      - POSTGRES_DB=demodb
    volumes:
      - db_data:/var/lib/postgresql/data
    networks:
      netmaker_network:
        ipv4_address: 172.20.0.11
    command: >
      sh -c "apk add --no-cache openssh &&
             ssh-keygen -A &&
             echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config &&
             echo 'root:demopass' | chpasswd &&
             /usr/sbin/sshd &&
             docker-entrypoint.sh postgres"

  webserver:
    image: nginx:alpine
    container_name: demo_web
    volumes:
      - ./demo_page:/usr/share/nginx/html
    networks:
      netmaker_network:
        ipv4_address: 172.20.0.12
    command: >
      sh -c "apk add --no-cache openssh &&
             ssh-keygen -A &&
             echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config &&
             echo 'root:demopass' | chpasswd &&
             /usr/sbin/sshd &&
             nginx -g 'daemon off;'"

networks:
  netmaker_network:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/16
          gateway: 172.20.0.1

volumes:
  db_data:

The Netclient service acts as an egress gateway, providing secure access to your Docker network through Netmaker's encrypted overlay network.

We have shown how a home lab or a local network can be accessed securely using Netmaker.

As an example we have a Database Service and a Web Server. Both can be accessed securely over SSH. To know more on fortifying your Docker containers check our guide on How to SSH into a Docker Container.

Security benefits:

Network security

  • All services are behind the secure Netmaker VPN 
  • SSH access is encrypted and controlled
  • No direct exposure to the public internet
  • Fixed IP addresses prevent network conflicts

Access control

  • Centralized management through Netmaker
  • Fine-grained control over service access
  • Audit logging capabilities
  • Easy user and access management

Data Protection

  • Encryption for all traffic
  • Secure remote management via SSH
  • Users can be granted a limited level of access
  • Isolated environments for different parts of your infrastructure

Enhancing Docker Compose Networking with Netmaker

Netmaker offers a robust solution for managing container networking, addressing the limitations of Docker Compose's default network setup. By implementing Netmaker, you gain advanced control and security over your container networks. One of Netmaker's standout features is its ability to create encrypted, virtual networks that connect your Docker containers across different environments. This is particularly useful when you need to manage complex, multi-cloud deployments or ensure secure communication between services in different data centers.

Additionally, Netmaker simplifies network management with its user-friendly interface and automation capabilities. It supports both direct and mesh networking models, allowing you to choose the best configuration for your application needs. This flexibility ensures that you can easily adapt to changing requirements or scale your application without the headache of extensive network reconfiguration. To get started with enhancing your Docker Compose networks with Netmaker, sign up here and explore the possibilities of seamless and secure container networking.

Build Your Dream Network Architecture
Sign up for a 2-week free trial and experience seamless remote access for easy setup and full control with Netmaker.
More posts

GET STARTED

A WireGuard® VPN that connects machines securely, wherever they are.
Star us on GitHub
Can we use Cookies?  (see  Privacy Policy).