You've just spun up a fresh VPS. The provider's welcome email sits in your inbox. You SSH in, see a blank Ubuntu prompt, and freeze.
This is where most setups go sideways. You either skip the hard parts (SSH keys, firewall rules, monitoring) or spend three hours tweaking things you'll forget about next month. Then something breaks at 2 AM and you're staring at logs with no baseline to compare against.
I've rebuilt servers enough times to know what matters on day one. This VPS initial server setup checklist is the runbook I follow every time—no guessing, no skipped steps.
Secure SSH Access First
Before you do anything else, lock down SSH. A fresh VPS with password authentication is a honeypot.
Step 1: Generate a key pair on your local machine (if you don't have one).
ssh-keygen -t ed25519 -C "your-email@example.com"
Use Ed25519. It's faster, shorter, and more secure than RSA for new keys. Don't set a passphrase if you're automating deployments; do set one if this is your only key.
Step 2: Copy the public key to the server.
You'll still use password auth here—one last time.
ssh-copy-id -i ~/.ssh/id_ed25519.pub root@your.vps.ip
If ssh-copy-id isn't available on your OS, paste the contents of ~/.ssh/id_ed25519.pub into ~/.ssh/authorized_keys on the server manually. Make sure permissions are 700 for .ssh and 600 for authorized_keys.
Step 3: Disable password authentication and root login.
Edit /etc/ssh/sshd_config:
sudo nano /etc/ssh/sshd_config
Find and change these lines (or add them if missing):
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
X11Forwarding no
Restart SSH:
sudo systemctl restart ssh
Gotcha: Test the key login in a new terminal before closing your current session. If you lock yourself out, you'll need the provider's emergency console.
Create a Sudo User
You disabled root login, so you need a non-root account with sudo access.
sudo adduser deploy
sudo usermod -aG sudo deploy
Copy your SSH key to the new user:
sudo -u deploy mkdir -p /home/deploy/.ssh
sudo cp /root/.ssh/authorized_keys /home/deploy/.ssh/
sudo chown -R deploy:deploy /home/deploy/.ssh
sudo chmod 700 /home/deploy/.ssh
sudo chmod 600 /home/deploy/.ssh/authorized_keys
From now on, SSH as deploy:
ssh deploy@your.vps.ip
Set Up the Firewall
UFW (Uncomplicated Firewall) is the easiest choice on Ubuntu. It's a wrapper around iptables that doesn't require you to understand iptables syntax.
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw enable
That's it. Deny everything inbound except SSH. Allow everything outbound (you can tighten this later if needed).
If you're running a web server, add HTTP and HTTPS:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
Check your rules:
sudo ufw status numbered
Gotcha: Enable the firewall after you've allowed SSH, not before. Otherwise, you'll lock yourself out.
Update the System
Don't skip this. Unpatched systems get compromised within hours in some environments.
sudo apt update
sudo apt upgrade -y
sudo apt autoremove -y
If the upgrade includes a kernel update, reboot:
sudo reboot
Wait 30 seconds, then SSH back in.
Configure Hostname and Timezone
Set a meaningful hostname (not localhost or the provider's default):
sudo hostnamectl set-hostname api-prod-01
Set the timezone. Most of my servers run in UTC, but if you're in a specific region:
sudo timedatectl set-timezone UTC
Verify:
timedatectl
Install Essential Tools
I always install these on day one:
sudo apt install -y curl wget git htop tmux
curlandwget: Download files and test APIs.git: Version control for config files. If you're planning to self-host your repositories, this guide on self-hosted git server alternatives is worth a read.htop: Interactive process monitor (better thantop).tmux: Terminal multiplexer—essential for long-running tasks and persistent sessions.
Set Up Monitoring and Logging
This is the step most people skip, then regret at 3 AM.
Enable journald persistence:
By default, systemd-journald logs to memory and discards old entries. Make logs persistent:
sudo mkdir -p /var/log/journal
sudo systemctl restart systemd-journald
Now logs survive reboots.
Install a simple metrics collector:
If you're using a monitoring service (Datadog, New Relic, Grafana Cloud), install their agent now. If you're self-hosting, install node_exporter:
wget https://github.com/prometheus/node_exporter/releases/download/v1.7.0/node_exporter-1.7.0.linux-amd64.tar.gz
tar xzf node_exporter-1.7.0.linux-amd64.tar.gz
sudo mv node_exporter-1.7.0.linux-amd64/node_exporter /usr/local/bin/
Create a systemd service file at /etc/systemd/system/node_exporter.service:
[Unit]
Description=Prometheus Node Exporter
After=network.target
[Service]
Type=simple
User=nobody
ExecStart=/usr/local/bin/node_exporter
Restart=on-failure
[Install]
WantedBy=multi-user.target
Start it:
sudo systemctl daemon-reload
sudo systemctl enable node_exporter
sudo systemctl start node_exporter
You can now scrape metrics from http://localhost:9100/metrics.
Configure Automatic Security Updates
You updated the system once. Now automate it:
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
Edit /etc/apt/apt.conf.d/50unattended-upgrades to enable automatic reboots if needed:
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "02:00";
Reboot at 2 AM on your least-critical day (usually Sunday).
Document Your Setup
This isn't optional. Write down:
- VPS IP address and provider.
- SSH key location on your local machine.
- Non-root user account name and sudo access.
- Firewall rules you've enabled.
- Installed monitoring agents and their endpoints.
- Scheduled tasks (backups, updates, etc.).
Store this in a README file in a private Git repo or a password manager. Future you will thank current you.
Verify Everything Works
Before you call it done, run through this checklist:
- SSH as the deploy user works; SSH as root is denied.
- Firewall is enabled and allows SSH, HTTP, and HTTPS (if needed).
- System is fully updated (
sudo apt update && sudo apt list --upgradable). - Hostname is set correctly (
hostname). - Timezone is correct (
date). - Monitoring agent is running (
sudo systemctl status node_exporteror equivalent). - Logs are persistent (
sudo journalctl --list-boots). - Automatic updates are enabled (
sudo systemctl status unattended-upgrades).
If all boxes are checked, you're done. You've got a hardened, monitored VPS that won't embarrass you at 2 AM.
What to Do Tomorrow
Don't stop here. This VPS initial server setup checklist covers the foundation, but you still need to:
- Set up backups (automated snapshots or rsync to another server).
- Configure log aggregation if you're running multiple servers.
- Add your VPS to your monitoring dashboard.
- Document any custom firewall rules or application-specific config.
The checklist above takes 30 minutes. Skipping it costs you 3 hours of debugging when something goes wrong. When choosing a database for your application stack, the PostgreSQL vs MongoDB real world comparison on techjournaler.com can help you make a more informed decision before you commit. Run through it every time you spin up a new VPS, even if you think you'll remember. You won't.