Laravel: Multiple websites on host share one external networked DB service container

@meyay
So this is what I want to achieve:

  • Create one network that is external and attachable on host. ( Let’s call this network “SKY” )
  • Create a database container that runs db mysql service and loads a volume
  • Create website-A that has 2 services defined, app, nginx. App is the laravel website files and nginx is the web server that processes requests and routes them to the app.
  • App is based on php-fpm image, and listens on port 9000
  • Nginx is based on nginx:latest image and forwards requests to app:9000
  • Website-B would be a clone of website-A, with the same app/nginx services.
  • The app service for both website-A and website-B need to connect to the db service on the external SKY network. Using DB_HOST=sky:db .env laravel configuration. ( Currently the db service is within the same docker compose application, so DB_HOST=db. But I want to connect to the db on the external SKY network.
  • I want website-A services ( app, nginx ) to be encapsulated , so that website-A and website-B do not collide when attempting to serve requests to port 9000.
  • Website-A exposes port 80 for localhost/ url
  • Website-B exposes port 81 for localhost:81 url

It seems that if website-A app and website-B app both have SKY listed in their networks: config, and both website-A and website-B app is listening on 9000 within that network, then nginx will be confused about which service to use for php files.

I created a separate network using docker network create with driver bridge and attachable.

How do I have web-A and web-B both connect to SKY:db service outside of their docker compose application? While still being able to encapsulate each website ( talking between php-fpm and nginx on port 9000 )

It would have been so lovely if you would have shared you compose file(s). I could just add some lines and everything would be clear. So then I will have to answer in prose as well.

  • traefik and nginx need to be in network, e.g. external
  • nginx and pfp-fpm need to be in another network, e.g. default,
  • pfp-fpm and the db need to be in another network e.g. sky.

Please do not generalize things like website-A, it makes it only unnecessary ambiogus. Be specific which container you mean (which to my understanding is pfp-fpm) .

I mentioned to share all involved compose files because it makes it much clearer what you actually configure and try to do, it also allows to reference the exact elements. So please share them.

I will have to re-write the files, to make them more general names that don’t reveal name of website etc. So after that I’ll share my project yml :slight_smile:

Please only redact domain names, ips, usernames, password and leave everything else as is. People tend to redact away things them deem irrelevant that often times are the cause for problems, result in ambiguity or introduce new problem that we concentrate on even though they don’t exist in the original file.

DATABASE docker-compose.yml

version: "3.8"
services:
  db:
    image: mysql:5.7
    container_name: app-database
    environment:
      MYSQL_DATABASE: ${DB_DATABASE}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_USER: ${DB_USERNAME}
      SERVICE_TAGS: dev
      SERVICE_NAME: mysql
    volumes:
      - ./docker-compose/secrets:/run/secrets
      - data:/var/lib/mysql
    networks:
      - sky-net
networks:
  sky-net:
    external: true

volumes:
  data:
    external: true
    name: domain_data

APP 1 docker-compose.yml

( The image jonwiz/php73-fpm-laravel is my custom Dockerfile built on top of php7.3-fpm from docker hub. )

version: "3.8"
services:
  app:
    image: jonwiz/php73-fpm-laravel:${PROJECT_TAG}
    container_name: website1-app
    working_dir: /var/www/
    volumes:
      - ./:/var/www
    networks:
      - net
      - sky-net
  nginx:
    image: nginx:latest
    container_name: website1-nginx
    ports:
      - ${PROJECT_PORT}:80
    volumes:
      - ./:/var/www
      - ./docker-compose/nginx:/etc/nginx/conf.d
    networks:
      - net

networks:
  net:
    driver: bridge
  sky-net:
    external: true

APP 2 docker-compose.yml

version: "3.8"
services:
  app:
    image: jonwiz/php73-fpm-laravel:${PROJECT_TAG}
    container_name: website2-app
    working_dir: /var/www/
    volumes:
      - ./:/var/www
    networks:
      - net
      - sky-net
  nginx:
    image: nginx:latest
    container_name: website2-nginx
    ports:
      - ${PROJECT_PORT}:80
    volumes:
      - ./:/var/www
      - ./docker-compose/nginx:/etc/nginx/conf.d
    networks:
      - net

networks:
  net:
    driver: bridge
  sky-net:
    external: true

LARAVEL .env db config
Both website applications use the same DB config to connect to db service on sky-net

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=my-database
DB_USERNAME=username
DB_PASSWORD=password

NGINX website config ( both websites use identical nginx conf )

server {
    listen 80;
    index index.php;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/src/public;


    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";


    error_page 404 /index.php;

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
    location / {
        try_files $uri $uri/ /index.php?$query_string;
        gzip_static on;
    }
    location = /favicon.ico {access_log off; log_not_found off;}
    location = /robots.txt {access_log off; log_not_found off;}

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

So since I’ve set this up, it appears to be working, as far as sharing the same sky-db network and app-database container with db service.

However the laravel_session cookies are competing with eachother. I login with one browser tab, the other logs out. I want to be able to keep the cookies separate and authenticate with both websites at the same time.
This way I can test multiple users logged in at the same time with same browser.

Maybe it’s a laravel config to override cookie name, and specify the port.

Since website1 is on localhost/ and website2 is on localhost:81

When one website is logged in, the other gets logged out and vice versa.

I think I have to setup nginx reverse proxy for my local development, to route domains to different containers.
I have nginx setup on my production host, but not on my local ubuntu 22.04 dev host.
So I believe using different domains will ensure my cookies don’t collide, and I can test the sites using different domains.

First of all, with the shared compose file the nginx to the app container can not happen, as they share a network the database is not attached to. From what I understand it was the original problem.

I can not say anything about how laravel works, though it sounds like a cookie problem.

Though, I don’t undertand why two distinct websites write in the same DB_DATABASE. This would solve the problem if session state is stored in the db.

Furthermore, shouldn’t distinct websites have distinct domain names? I assume this is you dev or lab environment? Why not introduce either a dns server into your environment that allows dns entry overrides (unbount, pihole?) or manipuation of the hosts file so that different domain names point to the same docker host + a reverse proxy to route traffic for the subdomain to the specific website? This would solve the problem is session state is stored in cookies.

I can not really help you with how laravel works.

Yes this is my local Ubuntu 22.04 dev machine.
I’m going to setup nginx in my local to reverse proxy to these containers on different ports.

These sites are the exact same website, clones, perhaps on different git branches to compare.
So I’ll route them to app1.test, and app2.test like this.

The nginx can route it’s requests to app service via port 9000.
Because php-fpm on the app service listens on port 9000.
Using the line fastcgi_pass app:9000;
Since nginx service only exists on the net network within the compose file.
Nginx knows to point to app service within that encapsulated network, and not try to find app on sky-net externally.
Since app has both net and sky-net networks listed, it can communicate with both nginx and db container.

Thanks so much for your quick replies :slight_smile:

Seems you are on track now :slight_smile:

If you are on Ubuntu 22.04 you could use dnsmasq to override domain entries or edit the good old /etc/hosts file.

Welcome!

Can a container network name have a colon tag?

example: my-net:dev

I would suggest trying those easy to test thing right away.

Here is how i test those kind of things:

cat <<'EOF' | docker compose -f - up 
services:
  test1:
    image: alpine:latest
    networks:
      "colon:net": {}
  test2:
    image: alpine:latest
    networks:
      "colon:net": {}
networks:
  "colon:net": {}
EOF

Output:

services.test1.networks Additional property colon:net is not allowed

Each service is registered in the build-in dns service by its service name, container name and host name. Additionally, it is registered by {service name}.{network name}.

As domain names do not allow colons, it makes sense that colons are not allowed.