Xavi.app Operations Manual (outdated)
Prerequisites
-
Ubuntu 24.04 LTS, RHEL, or compatible Linux server instance
-
Domain A record (e.g.,
cockpit.example.com) pointing to your server -
A non-root user with
sudoprivileges (e.g.,yourusername) -
Hardware virtualization enabled (VT-x/AMD-V) for VM management
(verify withegrep -c '(vmx|svm)' /proc/cpuinfo)
Install Cockpit and Extensions (Ubuntu & RHEL)
Cockpit (plus cockpit-machines for KVM VMs and cockpit-podman for containers) is in the default repositories of both Ubuntu 24.04 and RHEL 9 / Alma 9. Libvirt and QEMU packages are required for full VM support.
|
Step |
Ubuntu 24.04 LTS |
RHEL 9 / Alma 9 |
Description |
|---|---|---|---|
|
1 – Refresh repos |
sudo apt update |
sudo dnf update -y |
Update package index |
|
2 – Install Cockpit |
sudo apt install -y \ cockpit cockpit-machines cockpit-podman |
sudo dnf install -y \ cockpit cockpit-machines cockpit-podman |
Web console, VM module, container module |
|
3 – Install libvirt / QEMU |
sudo apt install -y \ libvirt-daemon-system qemu-kvm |
sudo dnf install -y \ libvirt qemu-kvm virt-install |
Enables KVM hypervisor support |
|
4 – Start services |
sudo systemctl enable --now \ cockpit.socket libvirtd |
sudo systemctl enable --now \ cockpit.socket libvirtd |
Launch Cockpit & libvirt immediately |
|
5 – Open firewall |
# Ubuntu/nftables example sudo nft add rule inet filter input \ tcp dport 9090 accept |
# RHEL / firewalld sudo firewall-cmd --add-service=cockpit sudo firewall-cmd --add-service=cockpit --permanent sudo firewall-cmd --reload |
Allow Cockpit port 9090/TCP (optional) |
|
6 – Verify |
sudo systemctl status cockpit.socket sudo systemctl status libvirtd |
should showactive (running) |
|
Access the Cockpit Web Dashboard
-
Open
https://localhost:9090in your browser (substitute your domain or IP). -
Log in with your sudo-enabled user (root login is disabled in the web UI).
-
Click Turn on administrative access in the sidebar for privileged actions (requires re-entering your password).
Core Features in Cockpit
-
Overview:Monitor system health, CPU/memory usage, and basic configuration.
-
Storage:Manage disks, partitions, and file systems; view I/O stats and logs.
-
Services:Control systemd services and timers (start, stop, enable, etc.).
-
Networking:Configure interfaces, monitor bandwidth, and check logs.
-
Logs:Search and filter logs by severity, date, or source.
-
Accounts:Add, edit, or remove user accounts and groups.
-
Terminal:Access a web-based shell for CLI management.
-
Virtual Machines:Create and manage KVM/QEMU VMs with
cockpit-machines. -
Podman Containers:Visually manage containers (start, stop, logs) with
cockpit-podman.
Enable Google Authenticator (TOTP) for Cockpit on Ubuntu 24.04
Why Add MFA?
Cockpit exposes full system-level controls (terminal, VM power-off, package installs, etc.). Adding time-based one-time passwords (TOTP) significantly reduces the risk of compromise if your primary password is leaked.
Prerequisites
-
Sudo-enabled account on the Ubuntu host running Cockpit.
-
Google Authenticator, FreeOTP, or any TOTP-compatible app on your phone.
Step1 — Install Required Packages
sudo apt update sudo apt install -y libpam-google-authenticator libqrencode-dev |
Step2 — Generate Your TOTP Secret & Scratch Codes
google-authenticator -t -d -f -r 3 -R 30 -W -Q UTF8 |
-
Scan the displayed QR code with your authenticator app.
-
Record the fivescratch codesin a safe place—they bypass MFA if you lose the phone.
Step3 — Require MFA for Cockpit via PAM
# Append the Google-Authenticator PAM module to Cockpit's stack sudo bash -c 'echo "auth required pam_google_authenticator.so nullok" >> /etc/pam.d/cockpit' |
Note: Add the linebeforerestarting Cockpit, otherwise existing sessions may be interrupted.
Step4 — Restart Cockpit
sudo systemctl restart cockpit |
Step5 — Log In with MFA
-
Browse to
https://your-server:9090and sign in with your Linux user credentials. -
When prompted for the verification code, open your authentication app and enter the 6-digit TOTP.
Troubleshooting Tips
-
Locked Out? Log in via SSH and remove the last line from
/etc/pam.d/cockpit, then restart Cockpit. -
Multiple Admins? Each admin must run
google-authenticatorunder their own account. -
Scratch Codes Exhausted? Rerun the command in Step2 to regenerate a new secret.
Publish Cockpit Behind Nginx with Let’s Encrypt
The safest way to expose Cockpit publicly is to place it behind an Nginx reverse-proxy that terminates TLS on ports80/443 and forwards traffic to the local Cockpit service (TCP9090). The high-level workflow is:
-
Install Nginx and the Certbot Nginx plugin.
-
Create atemporaryHTTP-only virtual host (port 80) for
cockpit.example.com. -
Run
certbot --nginxto request a certificate & let Certbot update the vhost for SSL. -
Replace the generated
location /block with the Cockpit reverse-proxy stanza shown below, then reload Nginx.
1. Install Prerequisites
|
Ubuntu 24.04 |
RHEL 9 / Alma 9 |
|---|---|
sudo apt update sudo apt install -y nginx python3-certbot-nginx |
sudo dnf install -y nginx python3-certbot-nginx sudo systemctl enable --now nginx |
2. Create a Minimal Port-80 vHost
Save /etc/nginx/sites-available/cockpit.temp (Ubuntu) or /etc/nginx/conf.d/cockpit.temp.conf (RHEL):
server {
listen 80;
server_name cockpit.example.com;
root /var/www/html; # any directory; not used after TLS
}
|
Enable and test, then reload:
# Ubuntu sudo ln -s /etc/nginx/sites-available/cockpit.temp \ /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx |
3. Obtain the Certificate
sudo certbot --nginx -d cockpit.example.com -m admin@example.com --agree-tos |
-
Certbot inserts a new
serverblock withlisten 443 ssl, certificate paths, and a redirect from HTTP→HTTPS.
4. Add the Cockpit Proxy Stanza
-
Edit the new HTTPS server block (file path as created by Certbot) so that the
location /looks like this:
location / {
proxy_pass https://127.0.0.1:9090;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Prevent gzip mangling of ETags (Cockpit bug #5239)
gzip off;
}
|
When finished:
sudo nginx -t && sudo systemctl reload nginx |
5. Firewall / SELinux Notes
-
Ubuntu nftables: ensure
tcp dport {80,443}areaccept. -
RHEL firewalld:
sudo firewall-cmd --permanent --add-service=http --add-service=https
thensudo firewall-cmd --reload. -
If SELinux is enforcing, allow Nginx to proxy:
sudo setsebool -P httpd_can_network_connect 1 |
6. Renewals & Maintenance
Certbot installs a systemd timer that runs twice daily. Verify:
sudo systemctl list-timers | grep certbot |
You can test a dry-run any time:
sudo certbot renew –dry-run |
Secure Web Servers with Let’s Encrypt SSL Certificates (Certbot)
This is a general description of the various command features involved in using the Lets Encrypt Certbot to generate SSL Certificates.
|
Command |
Description |
|---|---|
sudo apt install -y snapd |
Install Snapd package manager |
sudo snap install core; sudo snap refresh core |
Update core snap |
sudo snap install --classic certbot |
Install Certbot |
sudo ln -s /snap/bin/certbot /usr/bin/certbot |
Create symlink for Certbot (if missing) |
sudo certbot certonly --standalone -d example.com -m admin@example.com --agree-tos |
Generate SSL certificate (default configuration) |
sudo cat /etc/letsencrypt/live/example.com/fullchain.pem /etc/letsencrypt/live/ example.com/privkey.pem > cockpit.cert |
Combine certificate and key |
sudo certbot --nginx -d www.example.com -d example.com -m admin@example.com --agree-tos |
Generate SSL certificate for NGINX reverse proxy. (suggested configuration) |
sudo systemctl restart nginx |
Restart nginx to apply the certificate |
sudo certbot renew --dry-run |
Test certificate renewal |
Automate Renewal:Certbot includes a systemd timer, but you can add a cron job for redundancy:
sudo crontab -e # Add: 0 0 * * * /usr/bin/certbot renew --quiet |
Installing Certbot with Nginx Module
For servers running Nginx (e.g., hosting web apps), use Certbot’s Nginx plugin to automate SSL setup for Nginx-hosted domains.
|
Command |
Description |
|---|---|
sudo apt install -y python3-certbot-nginx |
Install Certbot Nginx module |
sudo certbot --nginx -d example.com -d www.example.com |
Configure SSL for Nginx domains |
Run sudo certbot renew to renew all certificates.
Managing Virtual Machines via Cockpit (KVM/QEMU)
-
Ensure
libvirt-daemon-systemandcockpit-machinesare installed. -
In Cockpit, go to Virtual Machines.
-
ClickC reate New VM and fill in:
-
Name: Ubuntu-VM
-
Installation Source: /path/to/ubuntu.iso
-
OS:Ubuntu 24.04
-
Memory: 4096 MB
-
Storage: 20 GB
-
Start the VM and use the Console tab to install the OS.
-
Manage VM state and resources from the UI.
Troubleshooting: If VMs fail to start, verify hardware virtualization in BIOS and check sudo systemctl status libvirtd.
Managing Containers with Cockpit-Podman
-
In Cockpit, select Podman Containers.
-
Click Create Container and enter:
-
Name: nginx-test
-
Image: docker.io/library/nginx
-
Port Mapping: 8080 (host) to 80 (container)
-
Click Create and Run, then test at
http://your-server-ip:8080. -
View logs and manage the container in the UI.
Troubleshooting: If inaccessible, ensure port 8080 is open in your firewall.
Switch to Pure nftables (replace ufw / iptables wrappers)
Ubuntu 24.04 LTS
# 1 – Install native CLI (kernel modules already present)
sudo apt update
sudo apt install -y nftables
# 2 – Disable UFW if it’s running
sudo ufw disable
sudo systemctl mask ufw
# 3 – (Optional) Remove legacy iptables wrappers
sudo apt purge iptables iptables-persistent
# 4 – Enable nftables so your ruleset loads at boot
sudo systemctl enable --now nftables
RHEL 9 / Alma 9
# Install CLI and start service
sudo dnf install -y nftables
sudo systemctl enable --now nftables
# (Optional) remove iptables compatibility layer
sudo dnf remove iptables iptables-services
Optional Extras
| Package | Purpose |
|---|---|
conntrack / conntrack-tools |
CLI to view or flush the connection-tracking table. |
nftables-doc |
Offline man-pages and examples. |
python3-nftables |
Python bindings for automated rule generation. |
Key Take-aways
- No extra kernel modules –
nf_conntrack,nf_nat, etc., are built into the stock kernel and auto-load on demand. nftablespackage = user-space CLI + systemd service; that’s all you install.- Removing
ufw/iptablesis optional but prevents confusion between legacy wrappers and nativenftsyntax.
Firewall Configuration with nftables
Configure nftables to allow Cockpit and other services:
|
Command |
Description |
|
|---|---|---|
|
Edit nftables rules |
|
#!/usr/sbin/nft -f
###############################################################################
# COMPLETE EXAMPLE NFTABLES CONFIG
#───────────────────────────────────────────────────────────────
# • Host is a KVM hypervisor running libvirt’s default NAT network
# • Guest subnet: 10.0.0.0/24 (bridge interface: “virbr0”)
# • Public IPs :
# 203.0.113.2 ← MGMT_IP (SSH + Cockpit + HTTPS for the host)
# 203.0.113.3 ← NAT_IP1 (DNAT for multiple guests)
# 203.0.113.4 ← NAT_IP2 (DNAT for a single guest)
#
# Replace the addresses / interface names to suit your environment.
###############################################################################
###############################################################################
# FILTER TABLE (inet) – Host firewall, VM isolation, Fail2Ban hook
###############################################################################
table inet filter {
############################ Fail2Ban dynamic set #########################
set f2b_blocklist { # Filled by Fail2Ban actions
type ipv4_addr
comment "Fail2Ban—blocked sources"
}
chain f2b-chain { # Executes early (priority -1)
type filter hook input priority -1; policy accept;
ip saddr @f2b_blocklist drop
}
###########################################################################
chain input {
type filter hook input priority 0; policy drop;
# ── Hygiene ────────────────────────────────────────────────────────
ct state invalid drop
ct state {established,related} accept
iif lo accept
ip protocol icmp icmp type echo-request accept
ip6 nexthdr icmpv6 icmpv6 type echo-request accept
# ── Host-level services (SSH/HTTP/HTTPS/Cockpit) ───────────────────
ip daddr 203.0.113.2 tcp dport {22,80,443,9090} accept
# ── UDP reflection / amplification guard ───────────────────────────
ip daddr {203.0.113.3,203.0.113.4} udp dport {53,67} drop
# ── Public services DNAT → VMs (NAT_IP1) ───────────────────────────
ip daddr 203.0.113.3 tcp dport {23-28,80,443,1935,3306} accept
# ── Public services DNAT → single VM (NAT_IP2) ─────────────────────
ip daddr 203.0.113.4 tcp dport {22,80,443} accept
# ── DNS / DHCP from guests (libvirt NAT) ───────────────────────────
iif "virbr0" udp dport {53,67} accept
# ── Default: log (rate-limited) then drop ──────────────────────────
log prefix "NFT-INPUT-DROP: " group 1 limit rate 1/second drop
}
chain forward {
type filter hook forward priority 0; policy drop;
# Standard conn-tracking
ct state invalid drop
ct state {established,related} accept
# ── Isolate VM 10.0.0.130 from other guests ────────────────────────
ip saddr 10.0.0.130 ip daddr 10.0.0.0/24 iif "virbr0" oif "virbr0" drop
ip daddr 10.0.0.130 ip saddr 10.0.0.0/24 iif "virbr0" oif "virbr0" drop
# Guest ↔ Guest (except isolated VM)
iif "virbr0" oif "virbr0" accept
# Guest → Internet
iif "virbr0" oif {"bridge0","eno1","eno2"} accept
# Internet (post-DNAT) → Guest
iif {"bridge0","eno1","eno2"} oif "virbr0" accept
# ICMP inside libvirt network
ip saddr 10.0.0.0/24 ip protocol icmp accept
ip daddr 10.0.0.0/24 accept
# Catch-all
log prefix "NFT-FORWARD-DROP: " group 1 limit rate 1/second drop
}
chain output { # Host-originated traffic
type filter hook output priority 0; policy accept;
}
}
###############################################################################
# NAT TABLE (ip) – DNAT, SNAT, SYN-flood protection
###############################################################################
table ip nat {
chain prerouting {
type nat hook prerouting priority -100; policy accept;
# ───────────────── 1) GLOBAL SYN caps (TLS) ───────────────────────
ip daddr {203.0.113.3,203.0.113.4} tcp dport 443 tcp flags syn \
limit rate over 100/second burst 200 packets \
log prefix "NFT-SYNFLOOD-443-GLOBAL: " drop
# ───────────────── 2) Per-source caps for HTTP/HTTPS ──────────────
ip daddr 203.0.113.3 tcp dport {80,443} tcp flags syn \
meter syn80_1133 size 10k { ip saddr limit rate over 10/second burst 20 packets } \
log prefix "NFT-SYN-80/443-1133: " limit rate 1/second drop
ip daddr 203.0.113.4 tcp dport {80,443} tcp flags syn \
meter syn80_1134 size 10k { ip saddr limit rate over 10/second burst 20 packets } \
log prefix "NFT-SYN-80/443-1134: " limit rate 1/second drop
# ───────────────── 2b) Per-source caps for SSH ────────────────────
ip daddr 203.0.113.2 tcp dport 22 tcp flags syn \
meter synssh_host size 10k { ip saddr limit rate over 5/second burst 10 packets } \
log prefix "NFT-SYN-SSH-HOST: " limit rate 1/second drop
ip daddr 203.0.113.3 tcp dport 22-28 tcp flags syn \
meter synssh_pub1 size 10k { ip saddr limit rate over 5/second burst 10 packets } \
log prefix "NFT-SYN-SSH-PUB1: " limit rate 1/second drop
ip daddr 203.0.113.4 tcp dport 22 tcp flags syn \
meter synssh_pub2 size 10k { ip saddr limit rate over 5/second burst 10 packets } \
log prefix "NFT-SYN-SSH-PUB2: " limit rate 1/second drop
# ───────────────── 3) DNAT to internal guests ─────────────────────
# NAT_IP1 (203.0.113.3) → Multiple VMs
ip daddr 203.0.113.3 tcp dport 23 dnat to 10.0.0.209:22
ip daddr 203.0.113.3 tcp dport 24 dnat to 10.0.0.37:22
ip daddr 203.0.113.3 tcp dport 25 dnat to 10.0.0.215:22
ip daddr 203.0.113.3 tcp dport 26 dnat to 10.0.0.168:22
ip daddr 203.0.113.3 tcp dport 27 dnat to 10.0.0.128:22
ip daddr 203.0.113.3 tcp dport 28 dnat to 10.0.0.45:22
ip daddr 203.0.113.3 tcp dport 80 dnat to 10.0.0.209:80
ip daddr 203.0.113.3 tcp dport 443 dnat to 10.0.0.209:443
ip daddr 203.0.113.3 tcp dport 1935 dnat to 10.0.0.128:1935
ip daddr 203.0.113.3 tcp dport 3306 dnat to 10.0.0.209:3306
# NAT_IP2 (203.0.113.4) → Single VM
ip daddr 203.0.113.4 tcp dport {22,80,443} dnat to 10.0.0.130
}
chain postrouting {
type nat hook postrouting priority 100; policy accept;
# SNAT / masquerade – Guests → Internet
ip saddr 10.0.0.0/24 oif {"bridge0","eno1","eno2"} snat to 203.0.113.3
ip saddr 10.0.0.130 oif {"bridge0","eno1","eno2"} snat to 203.0.113.4
}
}
###############################################################################
# END OF FILE
###############################################################################
|
Example advanced nftables.conf
Key capabilities: Fail2Ban integration Baseline packet hygiene Host-level management access UDP reflection / amplification guard Public services to multiple guests (NAT IP-1) Public services to a single guest (NAT IP-2) Per-source & global SYN-flood protection Guest network isolation Completely blocks traffic to/from a specific VM (10.0.0.130) from other guests. Inter-guest & guest-Internet forwarding Allows guest-to-guest traffic (except isolated VM) and guest-to-Internet via uplinks. Accepts Internet traffic already DNATed to guests. Guest DHCP/DNS passthrough SNAT / Source NAT Logging & rate-limiting Commented, modular structure |
|
|
Flushes the Ruleset |
|
|
Applythe Ruleset Persistently, is loaded on system boot. |
|
|
Lists the active Ruleset |
|
|
Restart services that modify the nftables.conf |
Install and Configure Fail2Ban on Ubuntu 24.04 & RHEL 9
1. Install Packages
|
OS / Repo |
Command |
Description |
|---|---|---|
|
Ubuntu 24.04 |
sudo apt update sudo apt install -y fail2ban |
Install Fail2Ban from the main repo |
|
RHEL 9 / Alma 9 |
sudo dnf install -y epel-release sudo dnf install -y fail2ban |
Enable EPEL and install Fail2Ban |
2. Create jail.local
Enable basic SSH protection and integrate with nftables:
# Default banning action (e.g. iptables, iptables-new, # iptables-multiport, shorewall, etc) It is used to define # action_* variables. Can be overridden globally or per # section within jail.local file banaction = nftables-block banaction_allports = nftables-block # The simplest action to take: ban only action_ = %(banaction)s[port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"] # ban & send an e-mail with whois report to the destemail. action_mw = %(action_)s %(mta)s-whois[sender="%(sender)s", dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s"] # ban & send an e-mail with whois report and relevant log lines # to the destemail. action_mwl = %(action_)s %(mta)s-whois-lines[sender="%(sender)s", dest="%(destemail)s", logpath="%(logpath)s", chain="%(chain)s"] # # JAILS # [sshd] #mode = normal port = ssh,23,24,25,26,27,28 logpath = %(sshd_log)s backend = %(sshd_backend)s maxretry = 2 findtime = 10m bantime = 1d journalmatch = _SYSTEMD_UNIT=sshd.service + _COMM=sshd action = %(action_)s filter = sshd[mode=aggressive] [nftables-synflood] enabled = true filter = nftables-synflood logpath = /var/log/kern.log maxretry = 5 findtime = 300 bantime = 3600 journalmatch = _SYSTEMD_UNIT=nftables.service + PRIORITY=5 action = %(action_mwl)s [nftables-invalid] enabled = true filter = nft-invalid logpath = /var/log/kern.log maxretry = 5 findtime = 60 bantime = 6h journalmatch = _SYSTEMD_UNIT=nftables.service + PRIORITY=5 action = %(action_mwl)s [nftables-udpblock] enabled = true filter = nft-udpblock logpath = /var/log/kern.log maxretry = 10 findtime = 30 bantime = 1h journalmatch = _SYSTEMD_UNIT=nftables.service + PRIORITY=5 action = %(action_mwl)s [cockpit] enabled = true filter = cockpit journalmatch = _SYSTEMD_UNIT=cockpit.service port = 9090 maxretry = 3 findtime = 10m bantime = 12h action = %(action_mwl)s [recidive] enabled = true logpath = /var/log/fail2ban.log journalmatch = _SYSTEMD_UNIT=fail2ban.service + PRIORITY=5 backend = systemd bantime = 4w findtime = 1d maxretry = 3 action = %(action_mwl)s [pam-generic] enabled = true filter = pam-generic logpath = /var/log/auth.log # systemd backend will read journal anyway backend = systemd journalmatch = _SYSTEMD_UNIT=systemd-logind.service maxretry = 5 findtime = 10m bantime = 12h action = %(action_mwl)s |
3. Create /action.d/nftables-block.local
[Definition]
actionstart = nft add table inet filter 2>/dev/null || true
nft add chain inet filter f2b-chain { type filter hook input priority -1 \; policy accept \; } 2>/dev/null || true
nft add set inet filter blocklist { type ipv4_addr \; } 2>/dev/null || true
nft add rule inet filter f2b-chain ip saddr @blocklist drop 2>/dev/null || true
actionflush =
actionstop = nft flush set inet filter blocklist 2>/dev/null || true
actioncheck = nft list chain inet filter f2b-chain | grep -q '@blocklist' 2>/dev/null || true
actionban = nft add element inet filter blocklist { <ip> } 2>/dev/null || true
actionunban = nft delete element inet filter blocklist { <ip> } 2>/dev/null || true
[Init]
table = filter
table_family = inet
chain = f2b-chain
chain_type = filter
chain_hook = input
chain_priority = -1
addr_type = ipv4_addr
addr_set = blocklist
blocktype = drop
nftables = nft
|
4. Start & Enable the Service
sudo systemctl enable --now fail2ban sudo fail2ban-client status sshd # verify jail is active |
Delayed Restart for Fail2Ban (ensures jails reload after nftables)
On Ubuntu 24.04 we add aone-shotsystemd service that waits until the network and nftables.service are up, then restarts Fail2Ban 30seconds after boot. This guarantees all persistent bans are re-inserted into the live nftables ruleset on server boot.
1. Create the helper script: /usr/local/bin/restart-fail2ban-delayed.sh
|
Run:
sudo install -m 750 -o root -g root /usr/local/bin/restart-fail2ban-delayed.sh |
2. Create the systemd unit
|
3. Enable and start
|
4. Verify
|
|
Why this matters:
-
fail2banstarts early in the boot sequence; ifnftablesloads afterward, latent bans are not present in the kernel firewall. -
This service guarantees bans are re-applied after the nftables ruleset is fully loaded.
-
The solution is distro-neutral—works the same on RHEL 9/Alma 9 by copying the same unit and helper script.
Useful Commands
-
sudo fail2ban-client status– list all active jails. -
sudo fail2ban-client set sshd unbanip <IP>– manually unban an IP. -
Logs:
/var/log/fail2ban.log.
Install PHP 8.3-FPM and Essential Modules
Add Repositories (if required)
-
Ubuntu 24.04:8.3 is already in the main archive.
-
RHEL 9:Enable Remi PHP 8.3:
sudo dnf install -y https://rpms.remirepo.net/enterprise/remi-release-9.rpm sudo dnf module reset php -y sudo dnf module enable php:remi-8.3 -y |
2. Install FPM and Modules
|
OS |
Command |
|---|---|
|
Ubuntu 24.04 |
sudo apt install -y php8.3-fpm php8.3-{cli,common,mysql,gd,xml,curl,mbstring,zip,intl,imagick,opcache}
|
|
RHEL 9 / Alma 9 |
sudo dnf install -y php-fpm php-{cli,common,mysqlnd,gd,xml,curl,mbstring,zip,intl,imagick,opcache}
|
3. Enable & Tune FPM
# Start and enable: sudo systemctl enable --now php-fpm # Performance tuning (Ubuntu path shown): sudo sed -i 's/^;?pm.max_children = .*/pm.max_children = 20/' \ /etc/php/8.3/fpm/pool.d/www.conf sudo systemctl restart php-fpm |
Verify with php -v and systemctl status php8.3-fpm.
Install MariaDB 11 and phpMyAdmin
1. Add MariaDB Official Repository
# Ubuntu 24.04 sudo apt install -y software-properties-common curl curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup | sudo bash -- --mariadb-server-version=11.5 |
# RHEL 9 / Alma 9 sudo curl -o /etc/yum.repos.d/MariaDB.repo \ https://r.mariadb.com/downloads/mariadb_repo_setup && \ sudo bash /etc/yum.repos.d/MariaDB.repo --mariadb-server-version=11.5 |
2. Install and Secure MariaDB
|
Command |
Description |
|---|---|
sudo apt install -y mariadb-server # Ubuntu sudo dnf install -y MariaDB-server # RHEL |
Install server package |
sudo systemctl enable --now mariadb sudo mysql_secure_installation |
Start service & run security script |
3. Install phpMyAdmin (Manual Deploy)
cd /opt sudo wget https://www.phpmyadmin.net/downloads/phpMyAdmin-latest-all-languages.tar.gz sudo tar xzf phpMyAdmin-*-all-languages.tar.gz sudo mv phpMyAdmin-*-all-languages /var/www/html/phpmyadmin sudo cp /var/www/html/phpmyadmin/config.sample.inc.php \ /var/www/html/phpmyadmin/config.inc.php sudo nano /var/www/html/phpmyadmin/config.inc.php /* set a random 32-char blowfish secret */ sudo chown -R www-data:www-data /var/www/html/phpmyadmin sudo systemctl reload nginx |
-
Download latest release:
-
Create a
config.inc.php(quick setup): -
Set permissions and reload PHP-FPM / Nginx:
4. (Optional) Protect phpMyAdmin with Basic Auth
# Create password file
sudo htpasswd -c /etc/nginx/.htpasswd pmaadmin
# In your server block:
location /phpmyadmin {
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
# …existing PHP-FPM configuration…
}
|
Quick Reference: Components & Commands
|
Component / Tool |
Ubuntu 24.04 Install |
RHEL 9 / Alma 9 Install |
Main Config / URL / Menu |
|---|---|---|---|
|
Fail2Ban |
sudo apt install fail2ban |
sudo dnf install epel-release fail2ban |
|
|
PHP 8.3-FPM |
sudo apt install php8.3-fpm |
sudo dnf install php-fpm |
|
|
MariaDB 11 |
sudo apt install mariadb-server |
sudo dnf install MariaDB-server |
|
|
phpMyAdmin |
Download tarball → |
|
|
|
Cockpit |
sudo apt install cockpit |
sudo dnf install cockpit |
|
|
Cockpit-Machines |
sudo apt install cockpit-machines |
sudo dnf install cockpit-machines |
“Virtual Machines” menu |
|
Cockpit-Podman |
sudo apt install cockpit-podman |
sudo dnf install cockpit-podman |
“Podman Containers” menu |
|
Certbot (stand-alone) |
sudo snap install --classic certbot |
sudo dnf install certbot |
|
|
Certbot Nginx Module |
sudo apt install python3-certbot-nginx |
sudo dnf install python3-certbot-nginx |
|
|
Netplan (base) |
sudo apt install netplan.io |
– |
|
Netplan Management Commands (Ubuntu)
-
sudo netplan apply— apply current YAML configuration. -
sudo netplan try— test new config & auto-rollback on failure (press Enter to accept). -
sudo netplan generate— regenerate backend configs (NetworkManager/systemd-networkd) without applying. -
sudo netplan set <path.to.option>=<value>— change a single value in-place (useful for scripts). -
sudo netplan info— show renderer backends and version details. -
sudo netplan get— display the currently combined configuration tree.
Troubleshooting Common Issues
-
VM Fails to Start: Check BIOS virtualization settings and
libvirtdstatus. -
Container Not Accessible: Confirm
nftablesallows the mapped port. -
Certificate Issues: Test renewal with
sudo certbot renew –dry-runand check/var/log/letsencrypt.