Introduction: Linux Mint as a Server OS
Linux Mint is widely known as a user-friendly desktop distribution, but it also makes an excellent server platform. Built on Ubuntu's long-term support (LTS) repositories, Linux Mint inherits rock-solid stability, a vast package ecosystem, and predictable update cycles. For developers and small teams who want a familiar environment from desktop to server, Linux Mint Server Editionโor more commonly, a minimal installation of Linux Mint configured as a headless serverโoffers a practical middle ground between Debian's austerity and Ubuntu Server's cloud-centric design.
This tutorial walks you through the complete journey: from bare-metal ISO installation to a secure, production-ready server running web applications, databases, and automated services. Every step includes practical code you can copy and adapt to your own environment.
What Is a Linux Mint Server?
๐ Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →A Linux Mint server is simply a Linux Mint installation stripped of its desktop environment and repurposed for server workloads. There is no separate "Server Edition" ISOโinstead, you install the base system, then configure it to run headless services such as:
- Web servers โ Apache, Nginx, or Caddy
- Database engines โ MySQL, MariaDB, PostgreSQL
- Runtime environments โ Node.js, Python, PHP, Go
- Container platforms โ Docker, Podman
- Monitoring stacks โ Prometheus, Grafana, Netdata
- File and backup services โ Samba, NFS, rsync, BorgBackup
Because Linux Mint tracks Ubuntu's LTS base (currently 24.04 Noble Numbat for Mint 22.x), you get five years of security updates without the need for rolling-release gymnastics. This makes it viable for production environments where stability trumps novelty.
Why Choose Linux Mint for a Server?
Several practical reasons make Linux Mint a compelling server choice:
- Ubuntu LTS core without Snaps โ Mint removes Ubuntu's snap-based package management by default, giving you pure APT-based package control without forced auto-updates.
- Familiar tooling โ If your development machine runs Mint, your server can run the exact same distribution, eliminating environment drift between local and remote environments.
- Cinnamon-free minimal footprint โ By installing without a desktop, the base system idles around 400โ600 MB RAM, comparable to Ubuntu Server.
- Strong community documentation โ The Ubuntu server documentation ecosystem applies almost verbatim to Mint.
- Predictable release cadence โ No surprise major version bumps; you control when to upgrade between LTS releases.
Step 1 โ Download and Prepare Installation Media
Download the Linux Mint ISO from the official site. Choose the 64-bit Cinnamon edition for familiarity, or the Xfce edition if you want a lighter fallback GUI. For a true headless server, you will later disable the graphical target.
Write the ISO to a USB drive using dd or a tool like Balena Etcher:
# Identify your USB device (e.g., /dev/sdb)
lsblk
# Write the ISO (replace paths accordingly)
sudo dd if=linuxmint-22-cinnamon-64bit.iso of=/dev/sdb bs=4M status=progress oflag=sync
Step 2 โ Base System Installation
Boot from the USB drive. When the installer launches, choose the minimal installation approach:
- Select your language and keyboard layout
- Choose Erase disk and install Linux Mint (or manual partitioning if you prefer LVM/ZFS)
- Create a strong root password and a separate user account with sudo privileges
- Opt out of proprietary drivers unless your hardware specifically requires them
After the installation completes and the system reboots, log in at the console. Your server is now running a full desktop environment, but we will convert it to a headless server shortly.
Step 3 โ Convert to Headless Mode
To save resources and align with server conventions, disable the graphical display manager and boot into multi-user console mode:
# Stop the display manager
sudo systemctl stop lightdm
# Disable it from starting on boot
sudo systemctl disable lightdm
# Set the default systemd target to multi-user (no GUI)
sudo systemctl set-default multi-user.target
# Reboot to apply changes
sudo reboot
After reboot, you will be greeted by a plain text console. Your server is now headless and conserves roughly 300โ500 MB of RAM compared to the desktop session.
Step 4 โ Network Configuration
Configure a static IP address so your server is always reachable at a predictable address. On Linux Mint (which uses NetworkManager by default even in multi-user mode), you can use nmcli or edit the Netplan configuration if you switched networking stacks.
Option A: Using NetworkManager (nmcli)
# List connections
nmcli con show
# Create or modify a static connection (adjust interface name, IP, gateway)
sudo nmcli con add con-name "static-eth0" ifname eth0 type ethernet \
ipv4.method manual ipv4.addresses 192.168.1.100/24 \
ipv4.gateway 192.168.1.1 ipv4.dns "8.8.8.8,1.1.1.1"
Option B: Using Netplan (if installed)
# Edit the Netplan YAML file
sudo nano /etc/netplan/01-netcfg.yaml
network:
version: 2
ethernets:
enp0s3:
addresses:
- 192.168.1.100/24
gateway4: 192.168.1.1
nameservers:
addresses: [8.8.8.8, 1.1.1.1]
# Apply the configuration
sudo netplan apply
Verify connectivity:
ip a show
ping -c 4 1.1.1.1
Step 5 โ System Update and Essential Packages
Immediately update the package index and install foundational server tools:
# Update package lists and upgrade existing packages
sudo apt update && sudo apt upgrade -y
# Install essential server utilities
sudo apt install -y curl wget git build-essential htop \
unzip zip ufw fail2ban tmux vim nano \
software-properties-common apt-transport-https \
ca-certificates gnupg lsb-release
These packages provide a comfortable administration environment and lay the groundwork for installing third-party software securely via HTTPS repositories.
Step 6 โ Create a Dedicated Administrative User
Never operate as root. Create a dedicated admin user with sudo privileges:
# Create a new user (replace 'opsadmin' with your preferred name)
sudo adduser opsadmin
# Add to sudo group
sudo usermod -aG sudo opsadmin
# Optionally add SSH keys for this user
sudo mkdir -p /home/opsadmin/.ssh
sudo chmod 700 /home/opsadmin/.ssh
sudo touch /home/opsadmin/.ssh/authorized_keys
sudo chmod 600 /home/opsadmin/.ssh/authorized_keys
sudo chown -R opsadmin:opsadmin /home/opsadmin/.ssh
Step 7 โ SSH Hardening
Secure Shell is your primary management channel. Harden it before exposing the server to any network:
# Edit the SSH daemon configuration
sudo nano /etc/ssh/sshd_config
Apply these changes:
# Disable root login over SSH
PermitRootLogin no
# Disable password authentication (use keys only)
PasswordAuthentication no
# Disable empty passwords
PermitEmptyPasswords no
# Use a non-standard port (optional, reduces log noise)
Port 22022
# Limit authentication attempts
MaxAuthTries 3
# Disable X11 forwarding (not needed on a server)
X11Forwarding no
# Allow only your admin user
AllowUsers opsadmin
Reload SSH to apply changes:
sudo systemctl reload sshd
Important: Before disabling password authentication, ensure you have copied your SSH public key to the server and tested key-based login successfully. Locking yourself out is a common pitfall.
Step 8 โ Configure the Firewall with UFW
UFW (Uncomplicated Firewall) provides a human-friendly interface to iptables. Enable it with a strict default-deny policy:
# Set default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH (adjust port if you changed it)
sudo ufw allow 22022/tcp
# Enable the firewall
sudo ufw enable
# Verify status
sudo ufw status verbose
Later, you will open additional ports (80, 443, database ports) as services are installed.
Step 9 โ Install and Configure Fail2ban
Fail2ban monitors logs and temporarily bans IPs that exhibit malicious behaviorโbrute-force SSH attempts, HTTP probing, etc.
# Enable and start fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
# Check status of the SSH jail
sudo fail2ban-client status sshd
Customize the SSH jail to match your non-standard port:
# Create a local override file
sudo nano /etc/fail2ban/jail.local
[sshd]
enabled = true
port = 22022
maxretry = 3
bantime = 3600
findtime = 600
sudo systemctl restart fail2ban
Step 10 โ Automatic Security Updates
Enable unattended upgrades so critical security patches apply without manual intervention:
sudo apt install -y unattended-upgrades
# Enable automatic updates
sudo dpkg-reconfigure --priority=low unattended-upgrades
Fine-tune the behavior by editing the configuration:
sudo nano /etc/apt/apt.conf.d/50unattended-upgrades
Ensure the Unattended-Upgrade::Allowed-Origins block includes security updates and optionally stable updates:
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}-security";
"${distro_id}ESM:${distro_codename}";
};
Step 11 โ Web Server Setup (Nginx + PHP-FPM)
Install Nginx as your reverse proxy and application server, along with PHP-FPM for dynamic content:
# Install Nginx and PHP-FPM with common extensions
sudo apt install -y nginx php-fpm php-mysql php-xml \
php-mbstring php-curl php-zip php-gd php-bcmath \
php-json php-intl php-imagick
Configure a sample virtual host
sudo nano /etc/nginx/sites-available/app.example.com
server {
listen 80;
server_name app.example.com;
root /var/www/app.example.com/public;
index index.php index.html;
access_log /var/log/nginx/app_access.log;
error_log /var/log/nginx/app_error.log;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.ht {
deny all;
}
}
# Enable the site
sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/
# Test configuration and reload
sudo nginx -t
sudo systemctl reload nginx
Open the HTTP port in the firewall:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
Step 12 โ SSL/TLS with Let's Encrypt (Certbot)
Secure your web traffic with free, automated TLS certificates:
# Install Certbot
sudo apt install -y certbot python3-certbot-nginx
# Obtain and auto-configure an SSL certificate
sudo certbot --nginx -d app.example.com
# Test auto-renewal
sudo certbot renew --dry-run
Certbot modifies your Nginx configuration to redirect HTTP to HTTPS and enables modern TLS settings. A systemd timer handles renewal automatically.
Step 13 โ Database Engine (MariaDB)
Install MariaDB, a drop-in replacement for MySQL with active development and better licensing:
sudo apt install -y mariadb-server mariadb-client
# Secure the installation
sudo mysql_secure_installation
During mysql_secure_installation, answer the prompts as follows:
- Set a strong root password
- Remove anonymous users โ Yes
- Disallow root login remotely โ Yes
- Remove test database โ Yes
- Reload privilege tables โ Yes
Create an application-specific database and user:
sudo mysql -u root -p
CREATE DATABASE app_production CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'strong_password_here';
GRANT ALL PRIVILEGES ON app_production.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Step 14 โ Node.js Runtime (via NodeSource)
For JavaScript-based applications, install Node.js from the official NodeSource repository to get a current LTS version:
# Add NodeSource repository (replace 20.x with desired LTS)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash -
# Install Node.js
sudo apt install -y nodejs
# Verify installation
node --version
npm --version
Install PM2, a production-grade process manager for Node.js applications:
sudo npm install -g pm2
# Start a sample application
pm2 start /var/www/app.example.com/server.js --name "app-backend"
# Save the process list for resurrection on reboot
pm2 save
# Enable PM2 to start on boot
pm2 startup systemd
sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u opsadmin --hp /home/opsadmin
Step 15 โ Docker and Containerization
Docker allows you to package applications with their dependencies into portable containers. Install Docker Engine from the official repository:
# Add Docker's GPG key and repository
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) \
signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
# Add your admin user to the docker group
sudo usermod -aG docker opsadmin
# Enable and start Docker
sudo systemctl enable docker
sudo systemctl start docker
Verify the installation:
docker run --rm hello-world
Step 16 โ Monitoring with Netdata
Real-time monitoring helps you detect anomalies before they become outages. Netdata provides a lightweight, out-of-the-box monitoring dashboard:
# Install Netdata via the official kickstart script
bash <(curl -Ss https://my-netdata.io/kickstart.sh) \
--stable-channel --disable-telemetry
# Netdata listens on localhost:19999 by default
# To access it remotely, set up an Nginx reverse proxy or SSH tunnel
Configure an Nginx reverse proxy for the Netdata dashboard (behind authentication):
sudo nano /etc/nginx/sites-available/netdata.example.com
server {
listen 80;
server_name netdata.example.com;
auth_basic "Netdata Dashboard";
auth_basic_user_file /etc/nginx/.htpasswd;
location / {
proxy_pass http://127.0.0.1:19999;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# Create HTTP basic auth credentials
sudo apt install -y apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd monitor_user
# Enable site and reload Nginx
sudo ln -s /etc/nginx/sites-available/netdata.example.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
# Obtain SSL certificate for the monitoring subdomain
sudo certbot --nginx -d netdata.example.com
Step 17 โ Automated Backups with BorgBackup
Production servers require automated, deduplicated backups. BorgBackup is efficient and encryption-capable:
sudo apt install -y borgbackup
# Initialize a Borg repository (local or remote)
borg init --encryption=repokey /backup-repo
# Create a backup script
sudo nano /usr/local/bin/daily-backup.sh
#!/bin/bash
set -e
export BORG_PASSPHRASE="your-strong-passphrase"
borg create --stats --compression lz4 \
/backup-repo::'{hostname}-{now:%Y-%m-%d}' \
/etc \
/var/www \
/var/lib/mysql \
/home/opsadmin
borg prune --keep-daily=7 --keep-weekly=4 --keep-monthly=6 /backup-repo
sudo chmod +x /usr/local/bin/daily-backup.sh
# Schedule via cron (runs daily at 2 AM)
echo "0 2 * * * root /usr/local/bin/daily-backup.sh >> /var/log/backup.log 2>&1" | \
sudo tee /etc/cron.d/borg-backup
Step 18 โ Log Management with Journald and Logrotate
Systemd's journal collects all service logs. Ensure persistent storage and configure rotation:
# Enable persistent journal storage
sudo mkdir -p /var/log/journal
sudo systemctl restart systemd-journald
# Configure log retention (edit /etc/systemd/journald.conf)
sudo nano /etc/systemd/journald.conf
[Journal]
Storage=persistent
SystemMaxUse=500M
MaxRetentionSec=30day
For application logs, configure logrotate:
sudo nano /etc/logrotate.d/custom-apps
/var/log/nginx/app_*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 0640 www-data www-data
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
}
Step 19 โ Kernel and System Hardening
Apply additional hardening via sysctl parameters:
sudo nano /etc/sysctl.d/99-hardening.conf
# IP spoofing protection
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Disable IP source routing
net.ipv4.conf.all.accept_source_route = 0
# Disable ICMP redirect acceptance
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
# Enable SYN flood protection
net.ipv4.tcp_syncookies = 1
# Disable IPv6 if not needed
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
# Apply immediately
sudo sysctl -p /etc/sysctl.d/99-hardening.conf
Step 20 โ Production Deployment Checklist
Before declaring your server production-ready, run through this verification checklist:
- SSH access confirmed โ Key-based login works; password authentication disabled
- Firewall active โ
sudo ufw status verboseshows only necessary ports open - Fail2ban running โ
sudo fail2ban-client statusshows active jails - Unattended upgrades enabled โ Security patches apply automatically
- SSL certificates deployed โ All public-facing services use TLS
- Database secured โ No anonymous users, strong passwords, localhost-only root access
- Backups scheduled โ Automated backup cron job exists and has been tested with a manual restore
- Monitoring accessible โ Netdata dashboard (or equivalent) reachable and collecting metrics
- Service restart policy โ Critical services enabled via systemd; PM2 resurrects on reboot
- Time synchronization โ
timedatectl statusshows NTP active and synchronized
Best Practices Summary
Throughout this tutorial, several recurring themes emerge. Keep these principles in mind as your server evolves:
- Principle of least privilege โ Every service, user, and process should have only the permissions it absolutely needs. Avoid running applications as root; use systemd service files to drop privileges.
- Defense in depth โ Firewall, Fail2ban, SSH hardening, and regular updates form overlapping layers. A single compromised layer should not grant full access.
- Immutable infrastructure mindset โ Document your setup steps (consider scripting them with Ansible or a shell provisioning script). Being able to rebuild from scratch in minutes is more valuable than heroic manual fixes on a live server.
- Regular testing โ Run restore drills for backups quarterly. A backup you haven't tested is not a backupโit's a hope.
- Log everything โ Centralize logs with journald, monitor them with fail2ban, and ship them off-server if possible. Disk space is cheap; forensic data during an incident is priceless.
Conclusion
You have now taken a stock Linux Mint installation from a desktop-oriented system to a hardened, headless production server capable of serving web applications, running containerized workloads, and surviving the harsh realities of the public internet. The journey involved stripping away unnecessary components, layering on security controls, installing modern runtime environments, and establishing automated monitoring and backup regimens.
Linux Mint's Ubuntu LTS foundation gives you enterprise-grade stability while its desktop heritage provides a comfortable fallback if you ever need to plug in a monitor for emergency troubleshooting. By following this guide, you have built a server that is secure, maintainable, and ready to handle real traffic. The next step is to codify this configuration into infrastructure-as-code using tools like Ansible, Terraform, or a simple shell provisioning scriptโso your next deployment takes minutes, not hours.