← Back to Home

VPS 配置避坑 Configuration Pitfalls Guide

VPSSSHUFWDockerFirewallUbuntu 开发环境ConfigurationPitfalls

Pitfall #1: Changing SSH Port But Forgetting Firewall Rules

First thing on a new VPS: change the SSH port from 22 (too many bots scanning). I changed it to 2222, restarted sshd, happily disconnected — then discovered: I could never connect again.

# Execute on VPS via VNC/console in provider control panel
sudo ufw allow 2222/tcp
sudo systemctl restart sshd

**Root cause**: Ubuntu 24.04 has UFW enabled by default (sudo ufw enable), with default deny incoming. Changing the SSH port doesn't automatically update the firewall rules.

**Verification**: RackNerd $2.24/month KVM VPS, Ubuntu 24.04 LTS default UFW status is inactive, but some provider images default to active. Confirm with sudo ufw status verbose.

Prevention: Add firewall rule BEFORE changing the port:

# Before changing port
CURRENT_PORT=$(grep ^Port /etc/ssh/sshd_config | awk '{print $2}')
sudo ufw allow $CURRENT_PORT/tcp comment "SSH port"

---

Pitfall #2: UFW Default Rules Break Docker Networking

After fixing SSH, I installed Docker following the official docs:

sudo apt update && sudo apt install docker.io
sudo systemctl start docker
sudo docker run -d -p 8080:80 nginx:alpine

Accessing http://your-vps-ip:8080 locally — **connection refused**.

# Check container status
sudo docker ps
# CONTAINER ID   IMAGE        STATUS
# abc123         nginx:alpine Up 2 minutes

# Check port listening
sudo ss -tlnp | grep 8080
# No output = not listening

**Root cause**: UFW's deny incoming blocks Docker's port mapping. Docker daemon creates its own iptables rules on startup, but if UFW starts after Docker, UFW rules override Docker's.

Solutions:

# Method 1: Open Docker port via UFW (recommended)
sudo ufw allow 8080/tcp comment "Docker nginx"

# Method 2: Disable UFW (testing only!)
sudo ufw disable

Verification: Docker official docs (docs.docker.com/engine/install/ubuntu) explicitly document UFW compatibility issues, verified April 2026.

---

Pitfall #3: Swap Too Small Causes MySQL OOM Kill

Deploying WordPress, set up LEMP stack (Linux+Nginx+MySQL+PHP). Importing a 2GB SQL file, MySQL crashes:

sudo dmesg | grep -i kill
# [  123.456] Out of Memory: Kill process 9876 (mysqld) score 892 or sacrifice child

Root cause: RackNerd $2.24/month plan has only 512MB RAM with no Swap. MySQL imports need temporary memory buffers — without Swap, it dies.

Solution: Add Swap file (2GB recommended for production):

# Create 2GB Swap
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

# Permanent: add to fstab
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

# Verify
free -h
#               total        used        free      shared  buff/cache   available
# Mem:          512Mi        256Mi        87Mi        12Mi       169Mi       187Mi
# Swap:         2.0Gi          0B       2.0Gi

Verification: AWS EC2 t3.micro (1GB RAM) has no Swap by default. Tests show MySQL importing SQL files over 500MB triggers OOM.

---

Pitfall #4: SSH Key Permissions Too Wide Gets Rejected

After configuring everything, set up passwordless login. Changed private key permissions to 777 (for easier debugging):

chmod 777 ~/.ssh/id_rsa

SSH connect again: Permission denied (publickey).

ssh -v -i ~/.ssh/id_rsa root@your-vps-ip
# debug1: key_load_public: no such file or public key in memory
# debug1: identity file /root/.ssh/id_rsa type 0
# Permission denied (publickey).

Root cause: SSH requires private key file permissions ≤600 (strict mode requires ≤400). 777 means "everyone can read/write" — SSH refuses to use the key.

Correct approach:

chmod 400 ~/.ssh/id_rsa      # Private key: owner read only
chmod 600 ~/.ssh/id_rsa      # Private key: owner read/write (equivalent)
chmod 700 ~/.ssh/            # Directory: owner only

**Verification**: OpenSSH 9.0+ (Ubuntu 24.04 default) enables PubkeyAuthentication yes by default, enforcing private key permissions ≤600.

---

Pitfall #5: Timezone Not Set Causes Certbot Renewal Failure

Certificate about to expire, manually renew:

sudo certbot renew --dry-run
# Challenge failed for example.com
# Error: invalid: {"type":"http-01","error":"unauthorized"}

After a day of debugging, discovered Let's Encrypt HTTP-01 challenge depends on accurate time — if your server time differs from CA time by hours, the CA rejects directly.

Root cause: Timezone error causes system time deviation. TLS handshake certificate verification and CRL checks both depend on correct time.

Diagnosis and fix:

# Diagnose
date                           # Current system time
timedatectl                    # Timezone and time status
# Example output: UTC 2026 Mon May 11 14:30:00 (should be Asia/Shanghai 22:30)

# Fix to CST
sudo timedatectl set-timezone Asia/Shanghai

# Verify
date
# Mon May 11 22:30:00 CST 2026

# Retry certificate
sudo certbot certonly --webroot -w /var/www/html -d example.com

**Verification**: Let's Encrypt CA server time is UTC-based, requiring request time error within ±24 hours. Certbot v5.5.0 (verified April 2026) outputs Authorization invalid instead of clear time error messages when timezone is wrong.

---

Complete Configuration Checklist (Avoid Repeat Mistakes)

Run in order after each new VPS deployment:

# 1. Timezone + time
sudo timedatectl set-timezone Asia/Shanghai
sudo apt install ntpdate && sudo ntpdate cn.pool.ntp.org

# 2. Swap (required for 512MB RAM plans)
sudo fallocate -l 2G /swapfile && sudo chmod 600 /swapfile
sudo mkswap /swapfile && sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

# 3. SSH port (add rule BEFORE changing)
sudo ufw allow 22/tcp comment 'SSH temp'
# Edit /etc/ssh/sshd_config Port 2222
sudo ufw delete allow 22/tcp
sudo ufw allow 2222/tcp comment 'SSH'
sudo systemctl restart sshd

# 4. UFW rules (after changing SSH port)
sudo ufw default deny incoming
sudo ufw allow 2222/tcp   # SSH
sudo ufw allow 80/tcp      # HTTP
sudo ufw allow 443/tcp     # HTTPS
sudo ufw enable

# 5. Docker (if needed)
sudo apt install docker.io
sudo usermod -aG docker $USER   # Add current user to docker group
sudo systemctl enable docker
sudo ufw allow 8080/tcp comment 'Docker app'  # Open your app port

---

Summary

These 5 pitfalls cover network, firewall, resources, permissions, and time — each cost me hours of debugging. Core lesson: VPS configuration is a chain reaction; any single failure cascades.

Related Articles:

**WordPress Plugin Development Book** — Learn to build WordPress plugins from scratch

👉 Try MiniMax API now: https://platform.minimaxi.com/subscribe/token-plan?code=E5yur9NOub&source=link

🔗 Related Tech Articles

Deep dive into related technical topics:

VPS Configuration Pitfalls Guide
技术标签: vps, ssh
VPS配置避坑指南
技术标签: vps, ssh
VPS配置避坑指南
技术标签: vps, ssh
🖥️ Best VPS Servers
查看推荐 →