Setting up a Linux server the right way from day one will save you countless hours of troubleshooting later. In this guide, I walk through every step I take when provisioning a fresh Ubuntu server for production — from the first SSH connection to having a fully secured, monitored environment ready to serve real traffic.
Prerequisites
Before you begin, make sure you have the following ready:
- A fresh Ubuntu 22.04 LTS server (VPS or bare metal)
- Root SSH access or a user with
sudoprivileges - A local SSH key pair — if you don't have one, generate it with
ssh-keygen -t ed25519 - A domain name pointed to your server's IP (optional but recommended for SSL)
This guide assumes you are connecting as root initially. We will fix that immediately.
Initial Server Setup
The very first thing you should do after logging in is update the system packages and configure a non-root user. Running as root all the time is a bad habit that will eventually cost you.
Creating a New User
Log in as root and run the following commands to create a dedicated admin user:
adduser datadmin
usermod -aG sudo datadmin
Now switch to the new user and verify sudo access:
su - datadmin
sudo whoami # should print: root
From this point forward, always log in as datadmin (or whatever you named the user) and use sudo when elevated privileges are needed.
Setting Up SSH Key Authentication
Password authentication is convenient but dangerous. Let's disable it and use SSH keys instead.
On your local machine, copy your public key to the server:
ssh-copy-id datadmin@your-server-ip
Now on the server, harden the SSH daemon config:
sudo nano /etc/ssh/sshd_config
Make sure these lines are set:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
Restart SSH to apply changes:
sudo systemctl restart sshd
Important: Before closing your current session, open a new terminal and confirm you can still log in with your key. If you lock yourself out, you'll need console access to fix it.
Configuring the Firewall
Ubuntu comes with ufw (Uncomplicated Firewall) which makes firewall management straightforward. Let's set up a basic ruleset.
Allow only the services you actually need:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
The output should show SSH, HTTP, and HTTPS allowed from anywhere. Everything else is denied by default.
If you're running other services like PostgreSQL or Redis, only expose them on 127.0.0.1 — never on the public interface unless you have a very good reason.
Installing and Configuring Nginx
Nginx is my go-to web server and reverse proxy for production environments. It's fast, battle-tested, and its configuration syntax is clean once you understand it.
Basic Nginx Installation
sudo apt update
sudo apt install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginx
Visit your server's IP in a browser — you should see the default Nginx welcome page.
Setting Up Virtual Hosts
Never put your config in the default site. Instead, create a dedicated config file for each domain:
sudo nano /etc/nginx/sites-available/yourdomain.com
Paste in a basic reverse proxy config (assuming your app runs on port 3000):
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Enable the site and reload Nginx:
sudo ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/
sudo nginx -t # always test before reloading
sudo systemctl reload nginx
Securing Your Server with SSL/TLS
There is absolutely no excuse for running HTTP-only in 2025. Let's Encrypt gives you free, auto-renewing certificates in under two minutes.
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Certbot will automatically modify your Nginx config to redirect HTTP to HTTPS and set up the certificate. It also installs a systemd timer that renews the cert before it expires.
Verify renewal works:
sudo certbot renew --dry-run
After this, your site is accessible via HTTPS with a valid certificate and automatic HTTP → HTTPS redirects.
Setting Up Automatic Updates
Security patches should be applied automatically. Manual updates are fine for major version upgrades, but you shouldn't have to babysit CVE announcements for routine patches.
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
Choose "Yes" when prompted. This enables automatic installation of security updates only (not regular package upgrades, which could break things).
Check the config file to understand what gets updated:
cat /etc/apt/apt.conf.d/50unattended-upgrades
You'll see it's scoped to Ubuntu:22.04 security repos — safe and focused.
Monitoring and Alerts
You can't fix what you can't see. A production server without monitoring is an accident waiting to happen. Here are the two tools I install on every server.
Installing Netdata
Netdata gives you beautiful, real-time metrics with zero configuration. It's the fastest way to get visibility into CPU, RAM, disk I/O, network, and more:
wget -O /tmp/netdata-kickstart.sh https://my-netdata.io/kickstart.sh
sudo sh /tmp/netdata-kickstart.sh --stable-channel
Once installed, Netdata runs on port 19999. Don't expose this publicly — access it via SSH tunnel:
ssh -L 19999:localhost:19999 datadmin@your-server-ip
Then visit http://localhost:19999 in your browser.
Setting Up Email Alerts
Configure Netdata to send you an email when something goes wrong. Edit the alarm notification config:
sudo nano /etc/netdata/health_alarm_notify.conf
Find the EMAIL_SENDER and DEFAULT_RECIPIENT_EMAIL lines and fill in your addresses:
EMAIL_SENDER="[email protected]"
DEFAULT_RECIPIENT_EMAIL="[email protected]"
Test the alert system:
sudo -u netdata /usr/libexec/netdata/plugins.d/alarm-notify.sh test
You should receive a test email within a few seconds.
Final Checklist
Before considering a server production-ready, go through this checklist:
- ✅ Root login disabled
- ✅ SSH key authentication only (password auth disabled)
- ✅ Firewall enabled — only ports 22, 80, 443 open
- ✅ Nginx installed and configured with virtual hosts
- ✅ SSL certificate issued and auto-renewal verified
- ✅ Automatic security updates enabled
- ✅ Monitoring running (Netdata or equivalent)
- ✅ Email alerts configured
- ✅ Backups configured (covered in a separate post)
This setup takes about 30 minutes on a fresh server and will serve you well for years. The key insight is that most server compromises and outages are completely avoidable — they happen because of skipped steps, not advanced attacks. Do the basics right and you're ahead of 80% of the internet.
In the next post, I'll cover automated backups with restic and off-site storage to an S3-compatible bucket. Stay tuned.