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.