Back to tutorials
Tutorial

Install phpMyAdmin on Ubuntu With Nginx and TLS

Install phpMyAdmin on Ubuntu with Nginx and TLS, then lock it down with auth and IP allowlists for safer DB admin.

By Anurag Singh
Updated on Oct 23, 2026
Category: Tutorial
Share article
Install phpMyAdmin on Ubuntu With Nginx and TLS

phpMyAdmin is convenient. That’s also why you should treat it as risky. A default install can turn “quick database admin” into a permanent, public attack surface.

This walkthrough shows you how to install phpMyAdmin on Ubuntu behind Nginx with HTTPS.

You’ll then restrict access with Basic Auth and an IP allowlist, so only you (or your team) can reach it.

These steps fit a real VPS or dedicated server. You’ll need control of the firewall, web server, and TLS.

If you want a clean Ubuntu box with root access for this setup, start with a HostMyCode VPS.

You’ll end up with a URL like https://pma.example.com/ that’s encrypted, password-gated, and not casually exposed to the whole internet.

What you’ll build (and what you need)

  • Ubuntu: 22.04 LTS or 24.04 LTS (commands are the same in this guide)
  • Web server: Nginx
  • PHP: PHP-FPM (Ubuntu packages; typically PHP 8.1+ on 22.04, PHP 8.3+ on 24.04)
  • Database: MySQL 8 or MariaDB (local or remote)
  • TLS certificates: Let’s Encrypt via Certbot
  • Hardening: Basic Auth + IP allowlist + sensible phpMyAdmin settings

Assumptions: you have a domain/subdomain ready.

Use a dedicated hostname like pma.example.com and point it to your server IP.

If you’re planning a DNS change window, lower TTL ahead of time. See DNS cutover tutorial for a VPS migration.

Step 0: Prep your Ubuntu server (packages, firewall, and basics)

SSH into your server and update packages:

sudo apt update
sudo apt -y upgrade
sudo reboot

After reboot:

sudo apt update

Install Nginx, PHP-FPM, and a few utilities:

sudo apt -y install nginx php-fpm php-mysql php-mbstring php-zip php-gd php-json php-curl php-xml unzip curl ca-certificates apache2-utils

Why apache2-utils? It provides htpasswd for Basic Auth.

If you’re running UFW, open only what you need.

For most setups, that’s SSH + HTTP + HTTPS.

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status verbose

If you want a stricter baseline, follow Set up UFW firewall on Ubuntu for a web server and return here.

Step 1: Create the phpMyAdmin hostname and verify DNS

Create an A/AAAA record for your subdomain (recommended):

  • pma.example.com → your server IPv4 (A record)
  • (Optional) AAAA record for IPv6

From your laptop (or anywhere), confirm DNS resolves:

dig +short pma.example.com A
# optional
# dig +short pma.example.com AAAA

You should see your server IP returned.

Don’t move on until DNS is correct.

Let’s Encrypt validation depends on it.

Step 2: Install phpMyAdmin (Ubuntu packages) and confirm PHP-FPM socket

Ubuntu ships phpMyAdmin as a package. It may not be the newest upstream release, but it’s maintained.

It also updates cleanly through normal system patching.

sudo apt -y install phpmyadmin

During installation, you may be asked about database configuration.

Many admins skip automatic dbconfig so database changes stay explicit and easy to audit.

If you enable dbconfig, keep the phpMyAdmin database user restricted. Don’t reuse it anywhere else.

Next, confirm which PHP-FPM socket you’re running.

On Ubuntu 24.04 it’s commonly /run/php/php8.3-fpm.sock. On 22.04 it’s often /run/php/php8.1-fpm.sock.

ls -l /run/php/

Write down the exact socket filename. You’ll reference it in Nginx.

Quick health check:

sudo systemctl status nginx --no-pager
sudo systemctl status php*-fpm --no-pager

Step 3: Install a dedicated Nginx server block for phpMyAdmin

Keep phpMyAdmin out of your main site config. Give it its own hostname and server block.

This makes lock-down easier, and it avoids unexpected side effects on your main site.

Create a new Nginx site file:

sudo nano /etc/nginx/sites-available/pma.example.com

Paste this configuration.

Then adjust server_name and the fastcgi_pass socket to match your system:

server {
    listen 80;
    server_name pma.example.com;

    root /usr/share/phpmyadmin;
    index index.php;

    access_log /var/log/nginx/pma.access.log;
    error_log  /var/log/nginx/pma.error.log;

    # Block hidden files (like .htaccess)
    location ~ /\. {
        deny all;
    }

    # phpMyAdmin entry
    location / {
        try_files $uri $uri/ =404;
    }

    # PHP handling
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;

        # IMPORTANT: set your PHP-FPM socket here
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }

    # Deny access to composer and other metadata if present
    location ~* /(composer\.(json|lock)|\.git) {
        deny all;
    }
}

Enable the site and test Nginx:

sudo ln -s /etc/nginx/sites-available/pma.example.com /etc/nginx/sites-enabled/pma.example.com
sudo nginx -t
sudo systemctl reload nginx

Verify: visit http://pma.example.com/. phpMyAdmin should load (not secure yet).

If you get a 502, your fastcgi_pass socket is wrong.

Re-check /run/php/.

Step 4: Add TLS with Let’s Encrypt (Certbot) and force HTTPS

Install Certbot with the Nginx plugin:

sudo apt -y install certbot python3-certbot-nginx

Request a certificate:

sudo certbot --nginx -d pma.example.com

When prompted, choose the option to redirect HTTP to HTTPS.

Verify the certificate and redirect:

curl -I http://pma.example.com/
curl -I https://pma.example.com/

You want a 301/308 redirect to HTTPS, then a 200 over HTTPS.

If you hit TLS snags (failed challenge, wrong DNS, mixed content), use the dedicated HostMyCode guide: TLS setup guide for Let’s Encrypt on a VPS.

Also confirm auto-renewal:

sudo certbot renew --dry-run

Step 5: Add Basic Auth (first lock on the door)

TLS encrypts traffic. It doesn’t stop login brute force.

Put Basic Auth in front of phpMyAdmin so the login page isn’t freely reachable.

Create an htpasswd file:

sudo htpasswd -c /etc/nginx/.htpasswd-pma adminuser

You’ll be prompted for a password. Use a long one.

If you add more users later, omit -c.

Edit your Nginx config. After Certbot runs, your server block is likely split into listen 443 ssl and a redirect block.

Add auth inside the server block that serves HTTPS.

sudo nano /etc/nginx/sites-available/pma.example.com

Add these lines inside the HTTPS server block.

Server-level is fine. Placing them in location / is a little cleaner.

auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd-pma;

Test and reload:

sudo nginx -t
sudo systemctl reload nginx

Verify:

curl -I https://pma.example.com/

You should see 401 Unauthorized without credentials.

Your browser should also prompt for a username/password.

Step 6: Add an IP allowlist (second lock, and it matters)

Basic Auth helps, but the endpoint still sits on the open internet.

If you have a fixed office IP, VPN egress IP, or static home IP, allowlist it. Block everything else.

Add an allowlist snippet file so it stays easy to manage:

sudo nano /etc/nginx/snippets/pma-allowlist.conf

Example (replace with your real IPs):

# Allow only these IPs to access phpMyAdmin
allow 203.0.113.10;      # office
allow 198.51.100.25;     # home
# allow 2001:db8:abcd::/64;  # optional IPv6 range

deny all;

Include it in the location / block (recommended) so it applies only to phpMyAdmin routes:

location / {
    include /etc/nginx/snippets/pma-allowlist.conf;
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd-pma;
    try_files $uri $uri/ =404;
}

Reload Nginx:

sudo nginx -t
sudo systemctl reload nginx

Verify: test from an allowed IP (it should load, then ask for Basic Auth).

Test from a non-allowed network (it should return 403 Forbidden).

If you travel often, a VPN is usually cleaner than constantly expanding the allowlist.

Step 7: Tighten phpMyAdmin configuration (blow away risky defaults)

On Ubuntu, phpMyAdmin configuration commonly lives under:

  • /etc/phpmyadmin/ (package config)
  • /etc/phpmyadmin/config.inc.php (main config)

Open the main config:

sudo nano /etc/phpmyadmin/config.inc.php

Add (or confirm) these settings:

$cfg['ForceSSL'] = true;
$cfg['ExecTimeLimit'] = 120;
$cfg['LoginCookieValidity'] = 1440; // 24 minutes
$cfg['ShowServerInfo'] = false;
$cfg['Servers'][1]['AllowNoPassword'] = false;

Notes that save you pain:

  • ForceSSL helps prevent accidental HTTP access if you later change Nginx rules.
  • AllowNoPassword = false blocks a surprisingly common misconfig where blank passwords slip in during testing.
  • Keep cookie lifetime short. phpMyAdmin sessions are not the place for “remember me for a week”.

Restart PHP-FPM to apply PHP-side config changes cleanly:

sudo systemctl restart php8.3-fpm
# or on 22.04:
# sudo systemctl restart php8.1-fpm

Step 8: Create a least-privilege database user for phpMyAdmin logins

Logging into phpMyAdmin as root is a fast way to turn a small mistake into a bad day.

Create a dedicated admin user with only the permissions you actually need.

Log into MySQL/MariaDB from the server:

sudo mysql

Create a user restricted to local connections (recommended if phpMyAdmin and DB are on the same server):

CREATE USER 'pma_admin'@'localhost' IDENTIFIED BY 'use-a-long-random-password';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER,
      CREATE VIEW, SHOW VIEW, TRIGGER, EVENT
ON your_database_name.* TO 'pma_admin'@'localhost';
FLUSH PRIVILEGES;

If you need to manage multiple databases, grant per database instead of *.*.

Avoid GRANT ALL PRIVILEGES ON *.* unless you truly mean “full server admin” and you accept the blast radius.

Exit:

exit

Verify: log into phpMyAdmin using pma_admin. Confirm you can do the intended work (run queries, manage tables).

You should not be able to manage global users unless you deliberately granted it.

If you’re still building your DB stack, pair this with proper backups.

HostMyCode has a practical MariaDB backup tutorial here: Install MariaDB and create secure backups on Ubuntu.

Step 9: Add safe Nginx headers (small change, measurable reduction in exposure)

For an admin-only tool like phpMyAdmin, you can be stricter than you would be on a public site.

Edit the HTTPS server block in /etc/nginx/sites-available/pma.example.com and add:

add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;

# HSTS (enable only if you're committed to HTTPS for this hostname)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

Reload:

sudo nginx -t
sudo systemctl reload nginx

Verify headers:

curl -I https://pma.example.com/

Step 10: Logging, monitoring, and quick diagnostics

Even with strong lock-down, treat a database admin UI as something you should watch.

  • Nginx access log: /var/log/nginx/pma.access.log
  • Nginx error log: /var/log/nginx/pma.error.log

Tail logs while you test logins:

sudo tail -f /var/log/nginx/pma.access.log /var/log/nginx/pma.error.log

To see top IPs hitting the endpoint:

sudo awk '{print $1}' /var/log/nginx/pma.access.log | sort | uniq -c | sort -nr | head

If you want alerting (CPU spikes during brute force, disk fill, bandwidth anomalies), add monitoring.

A good baseline is Netdata + external uptime checks: Server monitoring setup with Netdata and uptime checks.

For a lightweight “is it up?” monitor you can self-host, see: Self-host Uptime Kuma on a VPS with Nginx HTTPS.

Common mistakes that break phpMyAdmin (or make it unsafe)

  • Wrong PHP-FPM socket → Nginx returns 502 Bad Gateway. Fix fastcgi_pass to match /run/php/phpX.Y-fpm.sock.
  • Leaving it on your main domain → scanners find it faster, and it inherits your public caching/CDN rules. Put it on a separate hostname.
  • No IP allowlist → Basic Auth alone reduces risk, but you’re still public. An allowlist removes most internet noise.
  • Logging in as MySQL root → one bad query drops the wrong database. Use a scoped user.
  • Forgetting Certbot renewals → you get an expired cert on the one tool you rely on during an incident. Run the dry-run test and make sure systemd timers are active.

Summary: your secure phpMyAdmin setup checklist

  • Nginx vhost on a dedicated hostname (pma.example.com)
  • Let’s Encrypt TLS + forced HTTPS redirect
  • Basic Auth in front of the app
  • IP allowlist + deny all fallback
  • Least-privilege database user (not root)
  • Dedicated access/error logs for auditing

If you’re building this for a client site or a production workload, run it on infrastructure you control and can scale.

A managed VPS hosting plan from HostMyCode can handle patching and core maintenance while you keep control of your stack.

If you need a clean Ubuntu server for phpMyAdmin + Nginx, start with a HostMyCode VPS and add your domain in minutes. If you’d rather spend less time on OS updates and security baselines, managed VPS hosting gives you the same control with help keeping the system stable and current.

FAQ

Should I expose phpMyAdmin to the public internet?

Prefer not to.

If you must, put it behind HTTPS, Basic Auth, and an IP allowlist. If you travel, a VPN is usually cleaner than widening the allowlist.

Can I run phpMyAdmin for a remote database server?

Yes, but treat it as sensitive.

Use a database account limited to the specific schemas you manage, and allow remote DB connections only from your phpMyAdmin server IP.

What’s safer: phpMyAdmin or MySQL CLI?

For most admin tasks, the MySQL/MariaDB CLI over SSH is safer because it’s not a web-exposed app.

phpMyAdmin is fine if you lock it down and monitor access.

Why do I get 403 Forbidden after adding an allowlist?

Your current IP isn’t in the allow list, or you’re coming from IPv6 while you only allowlisted IPv4 (or the reverse).

Check your client IP and add the correct entry.

How do I confirm Certbot renewal is working?

Run sudo certbot renew --dry-run.

Also check systemctl list-timers | grep certbot to confirm the renewal timer is scheduled.