Jadi ceritanya, aku baru beli VPS murah ($5/bulan) dari DigitalOcean. Fresh Ubuntu server, kosong melompong. Gak ada Nginx, gak ada SSL, gak ada security setup.
Banyak yang gak tau cara setup server dari nol. Padahal gak serumit yang dibayangin. Di tutorial ini, aku bakal jelasin step-by-step dari fresh server sampai production-ready.
Yang bakal kamu dapet di akhir tutorial:
- Server aman dari serangan
- Nginx reverse proxy
- SSL gratis dari Let’s Encrypt
- Auto-renew SSL
- UFW firewall
- Basic monitoring
Prerequisites
- VPS dengan Ubuntu 22.04/24.04 (DigitalOcean, Vultr, AWS, dll)
- Domain name yang sudah pointing ke IP server
- Terminal/SSH client
Step 1: SSH ke Server
Kalau baru beli VPS, biasanya kamu dikasih root password via email.
ssh root@your-server-ip
Atau kalau pakai SSH key:
ssh root@your-server-ip -i ~/.ssh/your-key.pem
Step 2: Initial Server Setup
Update system
sudo apt update && sudo apt upgrade -y
Buat non-root user
JANGAN pakai root untuk daily use! Itu security risk besar.
# Buat user baru
adduser deploy
# Kasih sudo privileges
usermod -aG sudo deploy
# Copy SSH keys ke user baru (supaya bisa SSH tanpa password)
rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy
Test login dengan user baru:
# Buka terminal baru
ssh deploy@your-server-ip
Kalau udah bisa login, lanjut.
Set timezone
sudo timedatectl set-timezone Asia/Jakarta
Setup hostname
sudo hostnamectl set-hostname my-server
Edit /etc/hosts:
sudo nano /etc/hosts
Tambahin:
127.0.0.1 localhost
127.0.1.1 my-server
Step 3: Security Hardening
Enable firewall (UFW)
# Pastikan UFW aktif
sudo ufw status
# Allow SSH (PENTING! Kalau gak, kamu bisa lock out sendiri)
sudo ufw allow OpenSSH
# Allow HTTP & HTTPS
sudo ufw allow 'Nginx Full'
# Enable firewall
sudo ufw enable
Output:
Firewall is active and enabled on system startup
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
Nginx Full ALLOW Anywhere
Setup SSH security
Edit /etc/ssh/sshd_config:
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak # Backup dulu!
sudo nano /etc/ssh/sshd_config
Ubah setting ini:
# Disable root login
PermitRootLogin no
# Disable password auth (pakai SSH key only)
PasswordAuthentication no
# Change default port (opsional tapi recommended)
Port 2222
# Limit login attempts
MaxAuthTries 3
# Set idle timeout (10 menit)
ClientAliveInterval 300
ClientAliveCountMax 2
Kalau ganti port SSH, jangan lupa update UFW:
sudo ufw allow 2222/tcp
sudo ufw reload
Restart SSH:
sudo systemctl restart sshd
Install Fail2ban (anti brute force)
sudo apt install fail2ban -y
# Buat config lokal
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local
Config:
[DEFAULT]
bantime = 3600 # Ban 1 jam
findtime = 600 # Deteksi dalam 10 menit
maxretry = 5 # Max 5 gagal login
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
Step 4: Install Nginx
sudo apt install nginx -y
# Enable dan start
sudo systemctl enable nginx
sudo systemctl start nginx
# Test — buka browser, akses IP server
# Harus muncul "Welcome to nginx!" page
Basic Nginx Config
sudo nano /etc/nginx/sites-available/myapp
Untuk Node.js app:
server {
listen 80;
server_name myapp.com www.myapp.com;
# Untuk static files
location / {
root /var/www/myapp/dist;
try_files $uri $uri/ /index.html; # SPA support
}
# Proxy ke Node.js
location /api {
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_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_cache_bypass $http_upgrade;
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;
}
Untuk Python/Django:
server {
listen 80;
server_name myapp.com;
location /static/ {
alias /var/www/myapp/static/;
}
location /media/ {
alias /var/www/myapp/media/;
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Enable site:
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default # Hapus default
sudo nginx -t # Test config
sudo systemctl reload nginx
Step 5: SSL dengan Let’s Encrypt (Gratis!)
# Install certbot
sudo apt install certbot python3-certbot-nginx -y
# Dapatkan SSL certificate
sudo certbot --nginx -d myapp.com -d www.myapp.com
# Ikuti prompt:
# - Enter email (untuk notifikasi expiry)
# - Agree to terms
# - Share email (opsional)
# - Pilih redirect (pilih 2: redirect HTTP → HTTPS)
Selesai! SSL udah aktif dan auto-redirect HTTP → HTTPS.
Auto-Renew
Certbot udah setup auto-renew via cron. Cek:
sudo systemctl list-timers | grep certbot
Manual test:
sudo certbot renew --dry-run
Kalau gak error, auto-renew udah works.
Step 6: Install Runtime
Node.js (via nvm)
# Install nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc
# Install Node.js LTS
nvm install 20
nvm use 20
nvm alias default 20
Python
sudo apt install python3 python3-pip python3-venv -y
# Buat virtual environment
python3 -m venv /var/www/myapp/venv
source /var/www/myapp/venv/bin/activate
pip install -r requirements.txt
PM2 untuk Node.js Process Management
npm install -g pm2
# Jalankan app
cd /var/www/myapp
pm2 start dist/index.js --name myapp
pm2 save
pm2 startup # Auto-start saat server restart
Supervisor untuk Python
sudo apt install supervisor -y
# /etc/supervisor/conf.d/myapp.conf
[program:myapp]
command=/var/www/myapp/venv/bin/gunicorn myapp.wsgi:application
--bind 127.0.0.1:8000
--workers 3
directory=/var/www/myapp
user=deploy
autostart=true
autorestart=true
stderr_logfile=/var/log/myapp/error.log
stdout_logfile=/var/log/myapp/access.log
environment=PYTHONUNBUFFERED="1"
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start myapp
Step 7: Database
PostgreSQL
sudo apt install postgresql postgresql-contrib -y
# Mulai service
sudo systemctl enable postgresql
sudo systemctl start postgresql
Buat database dan user:
sudo -u postgres psql
-- Di psql prompt
CREATE USER myuser WITH PASSWORD 'yang-kuat-banget-123';
CREATE DATABASE mydb OWNER myuser;
GRANT ALL PRIVILEGES ON DATABASE mydb TO myuser;
\q
Test connection:
psql -U myuser -d mydb -h localhost
Redis (untuk caching)
sudo apt install redis-server -y
sudo systemctl enable redis-server
# Test
redis-cli ping
# PONG
Step 8: Deployment Script
Bikin script untuk deploy yang smooth:
#!/bin/bash
# deploy.sh — Jalankan di server
set -e # Stop on error
APP_DIR="/var/www/myapp"
BRANCH="main"
echo "🚀 Starting deployment..."
# Navigate
cd $APP_DIR
# Pull latest code
echo "📥 Pulling latest code..."
git pull origin $BRANCH
# Install dependencies
echo "📦 Installing dependencies..."
npm ci --production
# Build
echo "🔨 Building..."
npm run build
# Restart
echo "🔄 Restarting app..."
pm2 restart myapp
echo "✅ Deployment complete!"
echo "📊 App status:"
pm2 status
chmod +x deploy.sh
Sekarang tinggal ./deploy.sh setiap kali mau deploy.
Step 9: Basic Monitoring
Simple health check script
#!/bin/bash
# health-check.sh
URL="https://myapp.com/api/health"
DISCORD_WEBHOOK="your-webhook-url"
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" $URL)
if [ $RESPONSE -ne 200 ]; then
curl -H "Content-Type: application/json" \
-d "{\"content\":\"🔴 Server down! Status: $RESPONSE\"}" \
$DISCORD_WEBHOOK
echo "ALERT: Server returned $RESPONSE"
# Auto-restart
pm2 restart myapp
echo "Auto-restarted myapp"
else
echo "✅ Health check passed ($RESPONSE)"
fi
Add ke cron:
crontab -e
# Check setiap 5 menit
*/5 * * * * /home/deploy/health-check.sh >> /var/log/health-check.log 2>&1
Install htop untuk monitoring
sudo apt install htop -y
htop # Lihat CPU, RAM, processes
Pitfalls yang Sering Bikin Bingung
1. Gak bisa SSH setelah ganti port Pastiin UFW allow port baru SEBELUM restart SSH. Atau better, test di session baru dulu sambil session lama masih aktif.
2. Nginx 502 Bad Gateway App belum jalan atau salah port. Cek:
pm2 status # Pastikan app running
curl localhost:3000 # Test langsung
sudo tail -f /var/log/nginx/error.log
3. SSL expired Biasanya karena auto-renew gagal. Cek:
sudo certbot renew --dry-run
# Kalau error, fix dulu, baru renew
sudo certbot renew
4. Disk space habis
df -h # Cek disk usage
sudo du -sh /var/log/* # Cek log sizes
sudo journalctl --vacuum-time=7d # Clean old logs
sudo apt autoremove # Hapus unused packages
Bonus: Deploy with GitHub Actions
Gabungin CI/CD + server setup:
name: Deploy to VPS
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /var/www/myapp
./deploy.sh
Checklist Server Production
Sebelum go live, pastikan semua ini done:
- Non-root user dengan sudo
- SSH key only (disable password login)
- UFW firewall enabled
- Fail2ban active
- Nginx reverse proxy configured
- SSL certificate installed
- Auto-renew SSL tested
- Database configured dengan strong password
- App runs as service (PM2/Supervisor)
- Health check monitoring active
- Log rotation configured
- Backup strategy in place
- Domain pointing ke server IP
Conclusion
Setup Linux server dari nol itu kayak masak rendang — banyak step, tapi hasilnya worth it banget. Dengan server yang properly secured dan configured, kamu bisa sleep tenang.
Jangan lupa: security itu process, bukan one-time setup. Update server secara berkala, monitor logs, dan review security config.
Ada pertanyaan soal server setup? Chat aku di Telegram, seru kalau bisa bahas bareng!