Search code examples
dockernginxdocker-composeminio

Minio console not accessible behind nginx reverse proxy


I am trying to redirect a example.com/minio location to minio console, which is run behind a nginx proxy both run by a docker compose file. My problem is that, when I'm trying to reverse proxy the minio endpoint to a path, like /minio it does not work, but when I run the minio reverse proxy on root path in the nginx reverse proxy, it works. I seriously cannot findout what the problem might be.

This is my compose file:

services:
  nginx:
    container_name: nginx
    image: nginx
    restart: unless-stopped
    ports:
      - 80:80
      - 443:443
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
      - ./log/nginx:/var/log/nginx/
  minio:
    image: minio/minio
    container_name: minio
    volumes:
      - ./data/minio/:/data
    command: server /data --address ':9000' --console-address ':9001'
    environment:
      MINIO_ROOT_USER: minio_admin
      MINIO_ROOT_PASSWORD: minio_123456
    ports:
      - 9000
      - 9001
    restart: always
    logging:
      driver: "json-file"
      options:
        max-file: "10"
        max-size: 20m
    healthcheck:
      test: ["CMD", "curl", "-f", "http://127.0.0.1:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3

My nginx configuration is like this:

server {
    listen 80;
    server_name example.com;

    # To allow special characters in headers
    ignore_invalid_headers off;
    # Allow any size file to be uploaded.
    # Set to a value such as 1000m; to restrict file size to a specific value
    client_max_body_size 0;
    # To disable buffering
    proxy_buffering off;


    access_log /var/log/nginx/service-access.log;
    error_log /var/log/nginx/service-error.log debug;

    location / {
        return 200 "salam";
        default_type text/plain;
    }
    location /minio {
        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_set_header Host $http_host;

        proxy_connect_timeout 300;
        # Default is HTTP/1, keepalive is only enabled in HTTP/1.1
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        chunked_transfer_encoding off;

        proxy_pass http://minio:9001;
    }
}

The picture I'm seeing of minio console at the domain is this: enter image description here

And the response of curling the endpoint ($ curl -k http://example.com/minio):

<null>
    <html lang="en">
        <head>
            <meta charset="utf-8" />
            <base href="/" />
            <meta content="width=device-width,initial-scale=1" name="viewport" />
            <meta content="#081C42" media="(prefers-color-scheme: light)" name="theme-color" />
            <meta content="#081C42" media="(prefers-color-scheme: dark)" name="theme-color" />
            <meta content="MinIO Console" name="description" />
            <link href="./styles/root-styles.css" rel="stylesheet" />
            <link href="./apple-icon-180x180.png" rel="apple-touch-icon" sizes="180x180" />
            <link href="./favicon-32x32.png" rel="icon" sizes="32x32" type="image/png" />
            <link href="./favicon-96x96.png" rel="icon" sizes="96x96" type="image/png" />
            <link href="./favicon-16x16.png" rel="icon" sizes="16x16" type="image/png" />
            <link href="./manifest.json" rel="manifest" />
            <link color="#3a4e54" href="./safari-pinned-tab.svg" rel="mask-icon" />
            <title>MinIO Console</title>
            <script defer="defer" src="./static/js/main.eec275cb.js"></script>
            <link href="./static/css/main.90d417ae.css" rel="stylesheet">
        </head>
        <body>
            <noscript>You need to enable JavaScript to run this app.</noscript>
            <div id="root">
                <div id="preload">
                    <img src="./images/background.svg" />
                    <img src="./images/background-wave-orig2.svg" />
                </div>
                <div id="loader-block">
                    <img src="./Loader.svg" />
                </div>
            </div>
        </body>
    </html>
    %

Solution

  • I also struggled with this for a long time and was finally able to resolve it.

    As far as I can tell, the key changes to make this work for me where:

    • Manually specifying a rewrite directive (instead of relying on the Nginx proxy_pass+URI behaviour which didn't seem to work for me).
    • Setting the resolver directive with short timeouts (so that rescheduling of services onto other nodes gets resolved).
    • Setting $upstream to prevent DNS caching.

    I had to change your setup a little bit so that now the Minio S3 API is served behind minio.example.com while the UI Web Console is accessible at minio.example.com/console/.

    I have edited your config files below:

    docker-compose.yml:

    services:
      nginx:
        container_name: nginx
        image: nginx
        restart: unless-stopped
        ports:
          - 80:80
          - 443:443
        volumes:
          - ./nginx.conf:/etc/nginx/conf.d/default.conf
          - ./log/nginx:/var/log/nginx/
      minio:
        image: minio/minio
        container_name: minio
        volumes:
          - ./data/minio/:/data
        command: server /data --address ':9000' --console-address ':9001'
        environment:
          MINIO_SERVER_URL: "http://minio.example.com/"
          MINIO_BROWSER_REDIRECT_URL: "http://minio.example.com/console/"    
          MINIO_ROOT_USER: minio_admin
          MINIO_ROOT_PASSWORD: minio_123456
        ports:
          - 9000
          - 9001
        restart: always
        logging:
          driver: "json-file"
          options:
            max-file: "10"
            max-size: 20m
        healthcheck:
          test: ["CMD", "curl", "-f", "http://127.0.0.1:9000/minio/health/live"]
          interval: 30s
          timeout: 20s
          retries: 3
    

    nginx.conf:

    server {
        listen 80;
        server_name minio.example.com;
    
        # To allow special characters in headers
        ignore_invalid_headers off;
        # Allow any size file to be uploaded.
        # Set to a value such as 1000m; to restrict file size to a specific value
        client_max_body_size 0;
        # To disable buffering
        proxy_buffering off;
    
    
        access_log /var/log/nginx/service-access.log;
        error_log /var/log/nginx/service-error.log debug;
    
    
        # Use Docker DNS
        # You might not need this section but in case you need to resolve
        # docker service names inside the container then this can be useful.
        resolver 127.0.0.11 valid=10s;
        resolver_timeout 5s;
    
        # Apparently the following line might prevent caching of DNS lookups
        # and force nginx to resolve the name on each request via the internal
        # Docker DNS.
        set $upstream "minio";
    
        # Minio Console (UI)
        location /console/ {
    
            # This was really the key for me. Even though the Nginx docs say 
            # that with a URI part in the `proxy_pass` directive, the `/console/`
            # URI should automatically be rewritten, this wasn't working for me.
            rewrite ^/console/(.*)$ /$1 break;
    
            proxy_pass http://$upstream:9001;
    
            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_set_header Host $http_host;
    
            proxy_connect_timeout 300;
    
            # To support websocket
            # Default is HTTP/1, keepalive is only enabled in HTTP/1.1
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            chunked_transfer_encoding off;    
        }
    
    
        # Proxy requests to the Minio API on port 9000
        location / {
    
            proxy_pass http://$upstream:9000;
    
            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_set_header Host $http_host;
    
            proxy_connect_timeout 300;
    
            # To support websocket
            # Default is HTTP/1, keepalive is only enabled in HTTP/1.1
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            chunked_transfer_encoding off;
        }
    
    }
    

    HTH!