Your First Hour With a New VPS: A Complete Bare-to-Secure Setup Guide (Make Your OpenClaw Safer)
A freshly bought VPS is like an unlocked door with script kiddies knocking from all over the world. This guide walks you through locking that door tight, so your openclaw won’t go running wild.
0. Before We Begin
You just bought a VPS, excitedly grabbed the IP and root password, and now what?
Most folks just dive right in: installing software and deploying their projects.
That’s a dangerous habit.
Your VPS has a public IP, meaning anyone in the world can try to connect to it. And with the default setup:
- Port 22 is open to the public
- The username is the universally known root
- Leaving just a single password to protect you
Countless automated scripts are scanning IP ranges every single day, brute-forcing common passwords. The moment your server goes live, it’s already under attack.
So, the very first thing you do after getting a VPS isn’t installing software—it’s locking down security.
1. Our Goal
The goal of this guide is:
- ✅ Set up secure remote access: SSH key login + a non-default port
- ✅ Create a standard user: avoid using root directly
- ✅ Configure a firewall: only open the ports you actually need
- ✅ Enable network acceleration: let BBR make your network fly
- ✅ Prevent brute-force attacks: fail2ban auto-bans malicious IPs
Once you’ve done all this, your VPS will have a rock-solid foundation, and you can deploy all sorts of services on it with total peace of mind.
2. Planning & Thinking
Why bother with all these configurations?
A freshly bought VPS is a lot like a brand-new unfinished house:
| Default State | Risk | Our Solution |
|---|---|---|
| Logging in directly as root | Too much privilege, high risk of accidental damage | Create a standard user + sudo |
| Password login | Vulnerable to brute-force attacks | SSH key authentication |
| Default port 22 | Prime target for port scanners | Switch to a non-standard port |
| Firewall disabled | All ports exposed to the internet | UFW: only allow essential ports |
| Native network | High packet loss, high latency | BBR congestion control algorithm |
The Overall Workflow

We’ll follow this exact order, and I’ll explain the “why” behind each step as we go.
3. Step-by-Step Guide
3.1 First Login & System Update
Grab Your Server IP
Once you’ve bought your VPS, your provider will give you:
- IP address: like
192.168.1.100 - Port: usually
22(some providers might change it to a different port) - Username: usually
root - Password: a randomly generated string

SSH Connection
Mac / Linux users: open your terminal and just type:
1 | ssh root@你的服务器IP |
Windows users:
- Windows 10/11 comes with OpenSSH built-in, so you can run the command above directly in PowerShell or CMD
- Or you can use tools like Termius, MobaXterm
The first time you connect, you’ll see a fingerprint confirmation prompt:
1 | The authenticity of host '192.168.1.100 (192.168.1.100)' can't be established. |
It’s basically asking: “I don’t recognize this machine, are you sure you want to connect?”
Type yes and hit Enter. Then enter your password (note: no characters will show up as you type, this is a normal security feature) and hit Enter.

If you see a prompt like this, you’re successfully logged in:
1 | Welcome to Ubuntu 24.04 LTS (GNU/Linux 6.x.x-x-generic x86_64) |
💡 Why verify the fingerprint?
This is to prevent “man-in-the-middle” (MITM) attacks. By saving the server’s fingerprint on your first connection, you’ll know something’s up if it ever changes down the road—usually a sign someone might be spoofing your server.

Getting to know your server
Once you’re logged in, let’s take a quick look at what this machine is packing:
1 | # 查看系统版本 |

Keep these details in mind—they’ll come in handy when troubleshooting later on.
System update
Alright, now that we’ve scoped out the server specs, the first real order of business is updating the system. Fresh server images can be a few months old, and there might be critical security patches waiting for you:
1 | # 更新软件包列表 + 升级所有软件 + 清理 |
Here’s what those flags do:
apt update: Updates the package indexapt full-upgrade: Upgrades all installed packages, including the kernel-y: Automatically says “yes” to prompts so you don’t have to babysit itapt autoremove: Cleans up dependencies that are no longer neededapt autoclean: Clears out the cached downloaded package files

⏱️ This step might take a few minutes, depending on how many updates are queued up. If it occasionally stops to ask for confirmation, just follow the prompts and you’ll be fine.
Checking if a reboot is needed
After a kernel update, you’ll usually need to reboot for it to take effect:
1 | # 检查是否需要重启 |

If it tells you a reboot is required:
1 | reboot |
Once it reboots, you’ll need to SSH back in.
Installing essential tools
1 | apt install -y sudo curl wget git vim htop tree unzip net-tools ufw fail2ban neofetch |
What these tools are for:
| Tool | What it’s for | |
|---|---|---|
sudo |
Lets regular users run admin commands | |
curl / wget |
Download files | |
git |
Version control | |
vim |
Text editor | |
htop |
A prettier process monitor | |
tree |
Show directory structure as a tree | |
net-tools |
Network tools (ifconfig, netstat, etc) | |
ufw |
Simple and easy firewall | |
fail2ban |
Brute-force protection | |
neofetch |
Display system info beautifully | |
![]() |
3.2 Creating a Non-Root User
Why not just use root?
The root user has way too much power. One typo with rm -rf / and everything is gone. For your daily operations, you should stick to a regular user and only use sudo when you actually need admin privileges.
The openclaw is a monster—would you really dare to hand root privileges over to it?
Creating the User
1 | # 创建用户(把 ittinker 换成你想要的用户名) |
The system will prompt you to set a password and fill in some basic info:
1 | New password: # 输入密码(不会显示) |

Granting sudo Privileges
1 | # 把用户加入 sudo 组, ittinker 换成你自己上面创建的那个用户 |
Let’s verify it:
1 | # 切换到新用户 ittinker 换成你自己上面创建的那个用户 |
If the output shows root, your sudo setup is good to go.

💡 Pro Tip
Type
exitto switch back to the root user. We’ll keep using root for the rest of the setup, and once everything is configured, we’ll switch back to the regular user.
3.3 Setting Up SSH Key Authentication
Key Authentication vs. Password Authentication

Key authentication is way more secure than passwords:
- Passwords can be brute-forced; keys are practically impossible to crack
- No need to type your password every time—much more convenient
- Even if your password leaks, no one can log in without the private key
Step 1: Generate a Key Pair Locally
Run this on your local machine (not the server):
1 | # 生成 ED25519 密钥(推荐,更安全更快) |
You’ll be prompted with:
1 | Enter file in which to save the key (/Users/你/.ssh/id_ed25519): |
Just hit Enter to accept the default path. Here we’re using ./ittinker, which simply means it’ll be saved in your current directory with the filename ittinker.
1 | Enter passphrase (empty for no passphrase): |
You can set a passphrase for the key (an extra layer of protection), or just hit Enter to leave it blank.

Once it’s done generating, you’ll see two files in the ~/.ssh/ directory (these are the default filenames you’ll get if you just hit Enter earlier):
id_ed25519: Your private key. Keep this absolutely safe and never leak it!id_ed25519.pub: Your public key. This is the one you’ll upload to the server.
💡 ed25519 vs RSA
ed25519is the currently recommended algorithm—it’s more secure and has a shorter key than traditional RSA. If your system is too old and doesn’t support it, you can fall back tossh-keygen -t rsa -b 4096.
A quick side note: ed25519 generates a much smaller key file, whereas RSA files are pretty bulky. I actually asked an AI about this—turns out the file size doesn’t really matter, it’s just how the algorithms work.
Step 2: Upload Your Public Key to the Server
Method A: Using ssh-copy-id (Recommended)
1 | # 在你的电脑上执行 |
After typing in your password, your public key will be automatically copied over to the server.

Method B: Manual Copy
If ssh-copy-id doesn’t work, you can just do it manually:
1 | # 1. 在你的电脑上,查看公钥内容 |
Copy the output (that long string of text starting with ssh-ed25519).
1 | # 2. 在服务器上,为新用户创建 .ssh 目录, ittinker 换成你刚刚创建的用户名 |
⚠️ Permissions are crucial!
SSH is super strict about file permissions:
.sshdirectory: 700 (only you can access it)authorized_keysfile: 600 (only you can read and write to it)If the permissions are off, SSH will refuse to use the key for login.
Step 3: Test your key-based login
Open a new terminal window (keep the original one open just in case you mess up and need a backup), and test the key login:
1 | ssh ittinker@你的服务器IP |
If you can log in without typing a password, your key setup is successful! 🎉
(If you set a passphrase for your private key, it’ll prompt you for that private key password—which is totally normal.)

We added the -i flag here because our key isn’t in the default path. We put it in the current directory instead. If you don’t specify it, the system will just try to use the default key.
3.4 Hardening SSH Security
Now that key-based login is up and running, let’s lock down SSH:
- Change the default port (to dodge automated port scans)
- Disable password authentication
- Block direct root login
Edit the SSH config file
1 | # 备份原配置 |

Find and tweak the following settings (some might be commented out, so just remove the leading #):
1 | # 修改端口(选一个 1024-65535 之间的数字) |

Save and exit (ctrl + o).
In Nano, you save by hitting ctrl + o. It’ll prompt you for the filename—just press Enter to confirm. To quit, hit ctrl + x.
Restart the SSH service
1 | systemctl restart sshd.service |
⚠️ Heads up!
Don’t close your current terminal just yet! First, open a new window and test if you can log in with the new setup:
1 ssh -p 22000 yourname@你的服务器IPIf it works, you’re good to close the old window. If you can’t log in, you can still fix the config from your original session.
If you bought a lightweight server, chances are you can’t change this port. Traditional ECS instances usually let you modify it, so just keep that in mind.
3.5 Set up the UFW firewall

UFW (Uncomplicated Firewall) is Ubuntu’s default firewall tool. It’s super simple to configure but packs a punch.
Basic setup
1 | # 设置默认策略:拒绝所有入站,允许所有出站 |
Enable the firewall
1 | ufw enable |
It’ll prompt you:
1 | Command may disrupt existing ssh connections. Proceed with operation (y|n)? |
Type y to confirm.
Check the status
1 | ufw status verbose |

23022 was a typo on my end—just swap in whatever works for your own setup.
Output looks like:
1 | Status: active |
Common Commands Cheat Sheet
1 | # 查看状态 |
3.6 Enabling BBR Acceleration
BBR (Bottleneck Bandwidth and Round-trip propagation time) is a TCP congestion control algorithm developed by Google that can significantly boost your network performance.
One-Click Enable
1 | # 添加 BBR 配置 |
Verify It’s Enabled
1 | # 查看当前拥塞控制算法 |
Output should be:
1 | net.ipv4.tcp_congestion_control = bbr |
1 | # 确认 BBR 模块已加载 |
Output looks like:
1 | tcp_bbr 20480 3 |
💡 What BBR Does
Once BBR is up and running, especially on unstable networks or high-latency connections, you’ll noticeably feel:
- Faster download speeds
- Smoother SSH sessions
- Quicker webpage loading

3.7 Setting Up fail2ban to Prevent Brute-Force Attacks
fail2ban monitors your logs and automatically bans any IPs that rack up too many failed login attempts.
Basic Configuration
1 | # 复制默认配置(不要直接修改 jail.conf) |
Find the [sshd] section and make sure the following settings are in place:
1 | [sshd] |

Start the service
1 | # 重启 fail2ban |
Check the status
1 | # 查看 fail2ban 状态 |

The output will look something like this:
1 | Status for the jail: sshd |
Common commands
1 | # 手动封禁 IP |
3.8 [Optional] Managing Multiple Servers with SSH Config
If you’re juggling multiple servers, typing out ssh -p 22000 user@ip every single time gets old fast. You can use SSH Config to make your life a lot easier.
The Config File
Edit ~/.ssh/config on your local machine:
1 | vim ~/.ssh/config |
Add your configurations:
1 | # 第一台服务器 |
How to Use It
Now you can connect directly using an alias:
1 | # 连接第一台服务器 |
No more memorizing IPs, ports, and usernames!
3.9 [Optional] Restrict Access to Cloudflare Only
If your site sits entirely behind Cloudflare, you can lock down ports 80 and 443 so only Cloudflare’s IPs can reach them. It’s a great little security boost.
⚠️ Heads up: This setup requires your domain to already be proxied through the Cloudflare CDN.
Auto-Configuration Script
1 | # 下载脚本 |
This script will:
- Grab the latest IP ranges straight from Cloudflare
- Add UFW rules to only allow those IPs to hit ports 80/443
- Reload UFW
Set Up Scheduled Updates
Cloudflare’s IP ranges can change over time, so let’s set this to auto-update every week:
1 | # 添加 cron 任务(每周一凌晨执行) |
3.10 Set Your Timezone
The default timezone is probably UTC. Let’s switch it to your local timezone so reading logs is a breeze:
1 | # 查看当前时区 |

3.11 [Optional] Set Up Automatic Security Updates
Let the system install security patches automatically so you don’t have to do it manually every time:
1 | # 安装自动更新工具 |
Select “Yes” to enable automatic updates.
💡 This will automatically install security updates only. It won’t upgrade your system to a major new release, so it’s pretty safe to leave on.
4. Wrap Up
Post-Setup Checklist
Once you’ve got all the above configured, run through this checklist to double-check everything:
| Check | Command | Expected Result |
|---|---|---|
| System is up to date | apt update && apt list --upgradable |
No packages to upgrade |
| Regular user can sudo | sudo whoami |
Outputs root |
| Key-based login works | ssh yourname@ip -p port |
Logs in without a password |
| Password login is disabled | grep PasswordAuth /etc/ssh/sshd_config |
PasswordAuthentication no |
| SSH port changed | grep Port /etc/ssh/sshd_config |
The port you set |
| Firewall is enabled | ufw status |
Status: active |
| BBR is enabled | sysctl net.ipv4.tcp_congestion_control |
= bbr |
| fail2ban is running | systemctl status fail2ban |
active (running) |
| Timezone is set | timedatectl |
Asia/Shanghai or your timezone |
| Local SSH Config | cat ~/.ssh/config |
Server alias configured |
Key File Locations
1 | /etc/ssh/sshd_config # SSH 服务端配置 |
Security Best Practices
- Update your system regularly:
apt update && apt upgrade - Check login logs regularly:
lastb(failed logins),last(successful logins) - Keep an eye on fail2ban bans
- Back up important config files before making changes
5. Troubleshooting
Q1: Key-based login failed, still asking for a password?
Possible reasons:
-
Permission issues (most common)
1
2
3# 检查并修复权限
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys -
Public key wasn’t copied correctly
1
2
3# 检查 authorized_keys 内容
cat ~/.ssh/authorized_keys
# 应该是一行完整的公钥,以 ssh-ed25519 或 ssh-rsa 开头 -
SELinux interference (on CentOS/RHEL systems)
1
restorecon -Rv ~/.ssh
Q2: Can’t connect after changing the port?
Troubleshooting steps:
-
Make sure the new port is open in your firewall:
1
ufw status | grep 你的端口
-
Double-check your SSH config:
1
grep Port /etc/ssh/sshd_config
-
Verify the SSH service is actually running:
1
systemctl status sshd
-
If you’re completely locked out, use your cloud provider’s VNC or web console to log in and fix it.
Q2.5: Port change not taking effect at all? (On lightweight cloud servers)
Symptom: You changed /etc/ssh/sshd_config, but after restarting the service, port 22 is still listening and the new port isn’t working.
How to diagnose it:
bash
1 | # 查看是谁在监听 22 端口 |
If the output looks something like this:
1 | tcp 0 0.0.0.0:22 0.0.0.0:* LISTEN 1/init |
It means port 22 is being listened on by init (PID 1), not the sshd process.
Why?: Some cloud providers’ “Lightweight Servers” or “Container Instances” proxy SSH at the platform layer, meaning your server’s internal sshd config won’t take effect at all.
The Fix:
| Instance Type | How to Change SSH Port |
|---|---|
| Traditional ECS | Edit sshd_config ✅ |
| Lightweight Server | Might need to be changed in the cloud console, or not supported at all |
| Container Instance | Usually not supported |
If your instance doesn’t support changing the port, other security measures become even more critical:
- ✅ Create a standard user + disable root login
- ✅ Use SSH key authentication + disable password login
- ✅ Use fail2ban to block brute-force attacks
Nail these down, and your server will still be highly secure.
Q3: fail2ban banned me by mistake?
1 | # 用 VNC 登录后解封 |
Q4: Website inaccessible after enabling UFW?
1 | # 检查 80/443 是否开放 |
Q5: “sudo: command not found” when running commands?
A minimal Debian install might not come with sudo. Install it using root:
1 | apt install sudo |
6. One-Click Init Script (For When You’re Comfortable)
Once you’re familiar with the steps above, you can use this script to fast-track the setup.
⚠️ Heads up: Before running the script, make sure you’ve already got your SSH public key ready.
1 |
|
7. How Much More Secure Are We Now?
| Step | What We Did | Why It Matters |
|---|---|---|
| System Update | Installed the latest patches | Fixes known vulnerabilities |
| Created a New User | Stopped logging in directly as root | Reduces the risk of accidental mishaps |
| SSH Keys | Swapped passwords for keys | Brute-force attacks become impossible |
| Changed the Port | Ditched the default port 22 | Lowers the chances of getting scanned |
| Disabled Passwords | Key-only login allowed | Completely eliminates password attacks |
| Disabled Root | Root can’t log in directly | Makes an attacker’s job much harder |
| UFW Firewall | Only opened necessary ports | Shrinks the attack surface |
| BBR Acceleration | Optimized network performance | Speeds up access |
| fail2ban | Auto-bans malicious IPs | Stops persistent attacks |
Once you’ve done all this, your server is already more secure than 90% of the VPS instances out there. Give yourself a pat on the back!
What’s Coming Up Next
In the next post, we’ll dive into a Quick Reference for SSH Key Login Setup, covering the nitty-gritty details like:
- How to generate keys on different operating systems
- Managing multiple keys
- SSH Agent configuration
- Common SSH client settings
This post was last updated: January 2026
Indie Dev Toolbox Series—hit that follow button!




