0

This is a question I am going to answer myself, because even in the world of AI it took me far too long to get this running.

The scenario is that I have

  • a fully qualified domain

  • a VPS service running ubuntu

  • nginx

  • mysql running on the machine, NOT from a docker container.

  • phpmyadmin running from a docker container

I ran into the error which has plagued many people is the mysqli_connect(): (HY000/2002): Connection refused error even though testing access directly on the machine proves that the users and passwords are correct.

There have been similar questions over time and even ChatGPT, Claude and others were not able to determine the answer as they all assumed that there were connections errors, but in reality, all it was was a port issue, that isn't obvious to solve.

Here is the setup:

Nginx

server {
    server_name database.your-domain.com;


    location / {
        proxy_pass http://127.0.0.1:8081;
        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_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;

        # Add these headers for secure cookies
        proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";

        # Optional: Set timeouts to prevent long idle connections
        proxy_connect_timeout 60s;
        proxy_read_timeout 60s;
        proxy_send_timeout 60s;
    }

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/database.your-domain.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/database.your-domain.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}

server {
    if ($host = database.your-domain.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen 80;
    listen [::]:80;
    server_name database.your-domain.com;
    return 404; # managed by Certbot
}

MySQL

The port and bind-address in mysql is

sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

#... set in file (you can adjust the port - but just make sure it is aligned with the phpmysql start script)

port = 3306
bind-address = 127.0.0.1

And restart mysql:

sudo systemctl restart mysql

MySQL User Setup:

sudo mysql -u root -p
CREATE USER 'testuser'@'127.0.0.1' IDENTIFIED BY 'created_user_password';
GRANT ALL PRIVILEGES ON *.* TO 'testuser'@'127.0.0.1';
FLUSH PRIVILEGES;
SELECT User, Host FROM mysql.user;

Result showing that I am creating a user that is restricted to access via a specified IP (in this case the IP of localhost)

+------------------+-----------+
| User             | Host      |
+------------------+-----------+
| testuser         | 127.0.0.1 |
| debian-sys-maint | localhost |
| mysql.session    | localhost |
| mysql.sys        | localhost |
+------------------+-----------+

Finally phpmyadmin

The startup config for docker run

#!/bin/bash

docker run -d \
--name phpmyadmin \
-p 127.0.0.1:8081:80 \
-e PMA_HOST=127.0.0.1 \
-e PMA_PORT=3306 \
-e PMA_ABSOLUTE_URI=https://database.your-domain.com/ \
phpmyadmin/phpmyadmin

This should be enough to get everything running, but phpmyadmin will allow you to access the login page, it will not provide the error message, but unfortunately it will hang without triggering anything other than a 504 timeout.

There will be no error messages that I could see.

In the answer I will outline the simple change and why it works.

1 Answer 1

0

In the example above phpmyadmin runs in docker and has a default port of 80.

This presents a conflict because you cannot also have nginx listening on port 80 (which is the default web service port), so the traditional way of solving this is to use docker port mapping as follows:

  1. nginx forwards external port 80 to port 443 (with a certificate upgrading to https), and then proxies to port 8081 (in the case described) on localhost.

  2. localhost 127.0.0.1:8081 is mapped to port 80 of the phpmyadmin docker container:

docker run -d \
--name phpmyadmin \
-p 127.0.0.1:8081:80 \
-e PMA_HOST=127.0.0.1 \
-e PMA_PORT=3306 \
-e PMA_ABSOLUTE_URI=https://database.your-domain.com/ \
phpmyadmin/phpmyadmin

Under normal circumstances this should be fine, but it leads to hanging states and after trying various ways to address this, including using open IP (0.0.0.0 which is very unsafe practice) and various port combinations.

It turns out that docker creats its own network configuration when you use the -p ip:port:port argument, and this interferes with the way phpmyadmin needs to connect to a database installed on the server.

To avoid docker from doing this I only had to override the port that phpmyadmin was actually using - in this case I replaced the port 80 with port 8081 with a environmental variable available in the phpmyadmin docker setup and set the network argument to host.

docker run -d \
--name phpmyadmin \
--network host \
-e APACHE_PORT=8081 \
-e PMA_HOST=127.0.0.1 \
-e PMA_PORT=3306 \
-e PMA_ABSOLUTE_URI=https://database.your-domain.com/ \
phpmyadmin/phpmyadmin

# restart the docker container
docker stop phpmyadmin && \
docker rm phpmyadmin

That basically makes the phpmyadmin container receive the requests forwarded by nginx without using the docker port mapping function and solved all the issues instantly.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.