4

I'm using docker-compose.yml for a couple services behind an nginx proxy on a Ubuntu VPS.

I'm having the issue where my Docker containers cannot reach each other using their publicly available proxy URLs. They can only reach each other using their Docker service names.

The easiest way to demonstrate is as follows. I can connect to my PostgreSQL instance from anywhere with the following:

psql -Atx postgres://username:pw@postgres.mysite.com/database

But if I enter one of my running containers:

docker compose exec backend sh

Then try to connect using the same connection string, it just hangs... There aren't any logs, even on the proxy side. But, I can make it work by using the Docker service name instead:

psql -Atx postgres://username:pw@postgres:5432/database

I should be able to use postgres.mysite.com from anywhere, outside or inside the container. But for some reason, I can only use it outside the container. Here's the nginx configuration section for this:

   upstream docker-postgres {
      server postgres:5432;
   }

   server {
      listen 443 ssl;
      server_name postgres.mysite.com;

      location / {
         proxy_pass http://docker-postgres;
      }
   }

But this is not related to PostgreSQL. This appears to be happening regardless of the service. Whenever container A (the app code) tries to connect to container B (the service) by using the publicly available URL of the proxy running in container C.

For example, for my MinIO service, uploads hang when uploading to https://assets.mysite.com, but not when uploading to http://minio:9001.

Partial docker-compose.yml file:

proxy:
    image: nginx:alpine
    ports:
      - '80:80'
      - '443:443'
    networks:
      - myNetwork
backend:
    ports:
      - '3000:3000'
    volumes:
      - ./:/app
      - /app/node_modules
    networks:
      - myNetwork
postgres:
    image: postgres:10.4
    ports:
      - "5432:5432"
    networks:
      - myNetwork
minio:
    image: minio/minio
    command: server --console-address ":9090" --address ":9001" ./minio_data
    ports:
      - '9090:9090'
      - '9001:9001'
    networks:
      - myNetwork

networks:
  myNetwork:
    external: true

Additional Information

I set up a /test endpoint in the proxy under the assets.mysite.com server_name.

location /test { return 200 'success!!'; }

I can hit this endpoint from the server (outside a Docker container):

curl https://assets.mysite.com/test → Success

However, I can't hit it from inside any container:

docker compose exec backend curl https://assets.mysite.com/test → Hangs!

Despite that, I can ping assets.mysite.com successfully, even from the containers:

docker compose exec backend ping assets.mysite.com

Here's nearly my whole nginx configuration as requested (just SSL certificates omitted)

http {
   server {
      listen 80 default_server;
      listen [::]:80 default_server;
      server_name _;

      location / {
         return 301 https://$host$request_uri;
      }
   }

   upstream docker-backend {
      server backend:9000;
      keepalive 100;
   }

   upstream docker-postgres {
      server postgres:5432;
   }

   upstream docker-redis {
      server redis:6379;
   }

   upstream docker-minio-assets {
      server minio:9001;
   }

   server {
      listen 443 ssl;
      server_name mysite.com;
      root /var/www/frontend;
      index index.html;

      location /test {
         return 200 'success!!';
      }

      location /backend {
         rewrite ^/backend(/.*)$ $1 break;
         proxy_pass http://docker-backend;
         proxy_set_header Host $host;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_set_header X-Forwarded-Proto $scheme;
         proxy_pass_request_headers on;
         client_max_body_size 3m;
      }

      location / {
         include /etc/nginx/mime.types;
         try_files $uri $uri/ /index.html =404;
      }
   }

   server {
      listen 443 ssl;
      server_name shopadmin.mysite.com;
      root /var/www/admin;
      index index.html;

      location /a/ {
         try_files $uri /index.html;
      }

      location / {
         include /etc/nginx/mime.types;
         try_files $uri /index.html =404;
      }
   }

   server {
      listen 443 ssl;
      server_name assets.mysite.com;
      client_max_body_size 20m; # Allow bigger file uploads.

      location /test {
         return 200 'success!!';
      }

      location / {
         proxy_pass http://docker-minio-assets;
      }
   }

   server {
      listen 443 ssl;
      server_name redis.mysite.com;

      location / {
         proxy_pass http://docker-redis;
      }
   }

   server {
      listen 443 ssl;
      server_name postgres.mysite.com;

      location / {
         proxy_pass http://docker-postgres;
      }
   }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
parliament
  • 21,544
  • 38
  • 148
  • 238
  • 1
    Are you able to ping `https://assets.mysite.com` from the backend container? The issue might be DNS-related. – Rob Jul 04 '23 at 08:05
  • @Rob At first I did appear to have some issue in my /etc/resolv.conf where the nameserver 9.9.9.9 was missing and that was preventing me from pinging anywhere. I have fixed this issue and can now successfully ping both assets.mysite.com and postgres.mysite.com but my issue persists. – parliament Jul 06 '23 at 07:46
  • @Rob I added some additional information at the end of the OP and started a bounty. Thank you – parliament Jul 06 '23 at 07:59
  • Maybe the issue relates to your https config. Can you try to replace `listen 443 ssl;` with `listen 80;` and make curl to http url? – Slava Kuravsky Jul 06 '23 at 08:36
  • To confirm that it is a docker network issue, can you [add a host network](https://stackoverflow.com/questions/24319662/from-inside-of-a-docker-container-how-do-i-connect-to-the-localhost-of-the-mach) to your container? If this works, it may also serve as a workaround, but is probably not ideal from a security perspective. – Rob Jul 06 '23 at 09:16
  • @SlavaKuravsky I set it to `listen 80;`, nothing changes. I can make the curl request from anywhere outside the container `curl http://assets.mysite.com/test` --> Success. But not "through" the container `docker compose exec backend curl http:/assets.mysite.com/test`, that hangs – parliament Jul 06 '23 at 10:40
  • This "hanging" looks like curl timeout. It happens also if the port (80/443) is closed by firewall. – Slava Kuravsky Jul 06 '23 at 11:38
  • You might be missing to enable ip forward, check with: `cat /proc/sys/net/ipv4/ip_forward` and if it returns `0` do `echo 1 > /proc/sys/net/ipv4/ip_forward` to enable it. The reason for this' that if you want to route those requests to the outside of docker networks' without using swarm, you have to enable IP Forwarding for the docker0 interface to actually bridge those requests correctly. – Marcel Jul 06 '23 at 14:08
  • Would [using, for testing, a dnsmaq](https://computingforgeeks.com/run-and-use-dnsmasq-in-docker-container/?expand_article=1#ez-clearholder-box-4) be a good option for you? Assuming the same docker network, your services would use `dnsmasq` in the `mydns` container as their DNS server, and `dnsmasq` will return the internal IP addresses of your services when queried with their public URLs. This should allow your services to communicate using the public URLs. – VonC Jul 06 '23 at 15:43
  • Just a suggestion you may be approaching this in a bit more complex way then you need to although there is nothing wrong with the way you are trying to do it. I would usually suggest the [12 factor app approach for backing services](https://12factor.net/backing-services) where you store your services urls in your environment variables. With this you could set your environment variables outside of docker to point to your proxy and using the env field in docker-compose for your services in docker you could set the urls for services as their docker service names. – John Jul 06 '23 at 17:08
  • I could write an answer focused on Node.js detailing the above. – John Jul 06 '23 at 17:08
  • are you using Linux? – Rhythm Shandlya Jul 06 '23 at 19:38
  • Can you `curl https://google.com`? Also try `bash -c "echo > /dev/tcp/google.com/443"` if you get back the prompt immediately that means the 443 port is reachable on google.com. Do the same for your yoursite.com. – zsolt Jul 07 '23 at 07:37
  • quick thought on your nameserver above... rather than messing with your /etc/resolv.conf directly, try something like this: `location / { resolver 9.9.9.9; proxy_pass https://assets.mysite.com; }` – Matt Jul 07 '23 at 17:12
  • I can see you're getting `assets.mysite.com` to resolve with a `ping`, but what is it resolving to? Depending on what that is, your reused network could have some issues with that routing. Otherwise, can you share more of your nginx config? We might be missing something else important.... – Matt Jul 07 '23 at 17:41
  • To answer several of your suggestions: @Marcel that command prints “1” both inside and outside the container VonC - no, as I have a similar setup already running without issue, I want to find what the actually problem is. Rob- likewise using host network_mode is not an acceptable workaround. John - I fail to see how this differs from my current approach. Besides, it works fine using the docker service names. However, with MinIO it must be the public url because the resulting file location derives from it, and using any sort of internal name will cause the file URL to be inaccessible. – parliament Jul 08 '23 at 06:11
  • Rhythm Shandlya - yes, linux @zsolt - I get back the prompt immediately for both google.com and mysite.com for the port 443. My firewall is already allowing this port, and I can reach all services with SSL, when requesting outside a container. Matt - It (expectedly) resolves to the IP of my host server. Tried adding the inline resolvers. No change. – parliament Jul 08 '23 at 06:11
  • I've added nearly full nginx.conf to OP – parliament Jul 08 '23 at 06:20
  • do you have the same issue if you replace the domain by direct ip adress ? – kevP-Sirius Jul 08 '23 at 07:16
  • @parliament I understand that the host network_mode is not an acceptable workaround, but I asked because if this would work it could point to the underlying problem. As many here in the comments and answers also assume that it is a DNS-related issue, it would be insightful to know at which point exactly DNS resolution fails. – Rob Jul 10 '23 at 06:43
  • I once had a firewall issue doing something similar, have you tried to enable access to all ports explicitly for the relevant interfaces? – Marcel Jul 10 '23 at 09:12
  • Maybe this issue relates to ssl certificates. Try to ignore it running `docker compose exec backend curl -k https://assets.mysite.com/test`. For further debug run curl with `-v` flag. – Slava Kuravsky Jul 10 '23 at 12:21
  • is it ipv6 only? I've seeing now some issues regarding ipv6 only servers running with `userland proxy` enabled. I'm also trying to fix an issue with one deployment and might have many intersections with your issue. – Marcel Jul 11 '23 at 15:32

3 Answers3

1

To resolve the issue of container A being redirected to the host instead of directly finding container B when resolving "b.domain.com", you can add an alias in the Docker Compose file for container B. Modify your Docker Compose file to include the following:

networks:
  my-net:
    aliases:
      - b.domain.com

By specifying the alias "b.domain.com" for container B within the "my-net" network, container A will be able to locate container B directly when resolving the hostname.

However, please note that if your containers are not part of the same network, Docker will install a UFW (Uncomplicated Firewall) rule that might block the connection between container A and container B. Therefore, ensure that both containers are within the same network or take appropriate measures to allow communication between them if they are on different networks.

Codemaker2015
  • 12,190
  • 6
  • 97
  • 81
0

I've had similar scenarios in the past, and there is one in particular that reminds me of your scenario, since you are having problems with subdomains, which was also the case for me. Please ignore my answer if it does not work for you, but I'll leave this here in case it helps anyone else, and avoids wasted time for them.

MY SCENARIO

I was running a Kubernetes (KIND) deployment, which uses a docker network, the same as in a docker compose setup. I needed to connect to AWS endpoints from containers and these were the external domain names:

  • authsamples.com (AWS Cloudfront)
  • login.authsamples.com (AWS Cognito)
  • web.authsamples.com (AWS Cloudfront)
  • api.authsamples.com (AWS API Gateway)

From within deployed containers I could make curl requests to the base domain of https://authsamples.com and also to general internet URLs such as https://google.com, but I received hangs when calling AWS subdomains such as https://login.authsamples.com. All URLs were resolvable from the host computer.

RESOLUTION

I tried quite a few updates to DNS and resolv.conf settings on both KIND worker nodes and containers. The strange behavior was that general internet access from containers was fine, and that the problem was specific to my subdomain based URLs.

There was nothing special about my setup, such as firewalls. The problem turned out to be at the host networking level. What worked for me was to ensure that Google's 2 main name servers were used on the host computer, against the TCP/IP connection, rather than using the automatic option.

DNS servers

OPERATING SYSTEMS

I have only experienced this type of issue on Windows and macOS, though I run Ubuntu as my main desktop these days. Without these DNS nameservers I have also sometimes experienced mobile development problems, eg Android WiFi network on simulators not working. To date I've not read anything that explains the behavior I was receiving.

THINGS TO TRY

When struggling with such issues, I would try quick actions like these, to see if any of this helps in your case.

  • Do you get the same problem calling from the NGINX container to the subdomain, eg https://assets.example.com? If so then NGINX is not the issue.

  • Can you make a curl request from services behind NGINX to the subdomain, eg https://assets.example.com (NO currently)?

  • Can you make a curl request from services behind NGINX to general internet URLs?

  • Can you make a curl request from services behind NGINX to the base domain, eg https://example.com?

  • Does changing the host networking to explicitly use Google nameservers change the behavior?

Gary Archer
  • 22,534
  • 2
  • 12
  • 24
  • Thanks but the answer doesnt apply. I'm setting up these services on a Ubuntu VPS. No UI. I believe my changes to include `nameserver 9.9.9.9` in /etc/resolv.conf already amount to this and did help to fix my ability to ping, but my issue remains. – parliament Jul 08 '23 at 06:24
  • What interests me about the question is that you are having problems connecting to DNS subdomains, similar to an issue I had. I updated my answer to better describe my issue, just in case any of the info is relevant to you. Your issue could be something different though, so please ignore it if so. – Gary Archer Jul 09 '23 at 09:52
-7

Update the DNS configuration of your Docker containers to use a DNS resolver which should resolve the domain names correctly. Then specify the DNS resolver in the docker-compose.yml file for each service by adding the dns option under the service definition.

Sreeram Nair
  • 2,369
  • 12
  • 27
  • 1
    Hi, Sreeram Nair. Most or all of your last 11 answers appear likely to be entirely or partially written by AI (e.g., ChatGPT). Many also have comments indicating that they are wrong in some respect. Please be aware that [posting AI-generated content is not allowed here](//meta.stackoverflow.com/q/421831). If you used an AI tool to assist with any answer, I would encourage you to delete it. We do hope you'll stick around and continue to be a valuable part of our community by posting *your own* quality content. Thanks! – NotTheDr01ds Jul 22 '23 at 22:31
  • 1
    **Readers should review this answer carefully and critically, as AI-generated information often contains fundamental errors and misinformation.** If you observe quality issues and/or have reason to believe that this answer was generated by AI, please leave feedback accordingly. – NotTheDr01ds Jul 22 '23 at 22:31