Simplifying Hostnames in Docker Compose for Laravel Apps
Tags: laravel, docker, devops, php
Published: 2021-10-13 04:00:04

You may come across weird situations in development using Docker, specifically docker compose, and the way networking is done. I use WSL2 with Docker Desktop to make my life easy and recently had to figure out aliases for hostnames so that containers could reference each other without an ip address or the service name, i.e. while using an http client to make a request from service a to service b.

Hopefully this will shed some light for people or just make it easier to use docker in general which is a great tech for development.

> this is not intended for production or staging use. for development purposes only

### Setting Up

I'll assume you have 3 laravel apps, and that NGINX is your proxy server (web server). That means we'll use PHP-FPM since all requests to your services are proxy passed from NGINX to the PHP-FPM service for the given application. FPM then runs the actual code and returns the response to NGINX.

We'll add in an assumed database service just so our docker-compose file looks complete. We'll walk through each part of the compose file paying more attention to the networking portions.

Lets pretend we have 3 laravel apps:
one each for management, payroll, and marketing.

```yml
services:
  management:
    build:
      context: .
    container_name: management
    image: laravel-example/management
    restart: unless-stopped
    tty: true
    working_dir: /var/www/management
    volumes:
      - ./:/var/www/management
    networks:
      - laravel-example
  payroll:
    build:
      context: ../payroll
    container_name: payroll
    image: laravel-example/payroll
    restart: unless-stopped
    tty: true
    working_dir: /var/www/payroll
    volumes:
      - ../payroll:/var/www/payroll
    networks:
      - laravel-example
  marketing:
    build:
      context: ../marketing
    container_name: marketing
    image: laravel-example/marketing
    restart: unless-stopped
    tty: true
    working_dir: /var/www/marketing
    volumes:
      - ../marketing:/var/www/marketing
    networks:
      - laravel-example
  nginx:
    image: nginx:latest
    container_name: nginx
    restart: unless-stopped
    tty: true
    ports:
      - 80:80
      - 443:443
    volumes:
      - ../payroll:/var/www/payroll
      - ../marketing:/var/www/marketing
      - ./:/var/www/management
      - ./build/dev/nginx/conf:/etc/nginx/conf.d
      - ./build/dev/nginx/certs/:/etc/nginx/ssl
    networks:
      laravel-example:
        aliases:
          - payroll.bigcorp.test
          - marketing.bigcorp.test
          - management.bigcorp.test
    depends_on:
      - marketing
  mariadb:
    image: mariadb:10.2
    container_name: mariadb
    ports:
      - 33065:3306
    environment:
      MYSQL_ROOT_PASSWORD: "password"
      MYSQL_DATABASE: "your_db"
      MYSQL_USER: "root"
      MYSQL_PASSWORD: "password"
      MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
    volumes:
      - laravel-mariadb:/var/lib/mysql
    networks:
      - laravel-example
    restart: on-failure
    healthcheck:
      test: ["CMD", "mysqladmin", "ping"]
networks:
  laravel-example:
    driver: bridge
    external: false
    name: laravel-example
volumes:
  laravel-mariadb:
    driver: local

```

The yaml file may seem large, but it's actually fairly easy to break down service by service and understand what each part does. Our main concern for this post is understanding the hostname aliases. Normally docker will assign an ip to a service based on its internal networking, so you'll never have the same ip address for a given service on a given build or run. Services can "talk" to each other by their name since docker will resolve the service name to an ip address. Where this breaks down is a service making an http request within the docker network to another service. The call will fail since the hostname will not resolve, there is no DNS server of course. To fix this we need to look at two things:

1. Our networks key in the compose file says "make me a network named laravel-example, and use the bridge driver, this is not an external (already existing network).

2. Each of our services is built in this network. The key to making the hostname resolve within docker like DNS would is the networks key in our NGINX service

```yaml
networks:
      laravel-example:
        aliases:
          - payroll.bigcorp.test
          - marketing.bigcorp.test
          - management.bigcorp.test
```

Basically we're just saying, "resolve any of these hostnames to the NGINX service". This works because of course all requests to NGINX get passed to server blocks that are proxied to a PHP-FPM service. We can look at how that works in a later post.

Hopefully this sheds some light on dockers networking in a practical way for development purposes.