Setup VPS Ubuntu 24.04 dari Nol untuk WordPress Panduan 2-Jam yang Saya Pakai untuk 12 Klien

Setup VPS Ubuntu 24.04 dari Nol untuk WordPress: Panduan 2-Jam yang Saya Pakai untuk 12 Klien

Diposting pada

Jumat sore, 17:43 WIB. WhatsApp masuk dari pemilik toko fashion muslim di Surabaya:

“Bro, gua butuh site live malam ini. Influencer X mau drop post promo jam 8 malam ke 240 ribu follower. Sekarang domain masih parking page. Bisa?”

Saya cek jam: 17:45. Drop time 20:00. Saya punya 2 jam 15 menit. Plus saya belum makan malam.

Saya buka playbook yang sudah saya pakai untuk 11 klien sebelumnya, buka tmux split di terminal, satu pane untuk SSH satu pane untuk tail -f log. Klik “Deploy Now” di dashboard Vultr 18:13 WIB. Saya catatkan timestamp di Notes. Stopwatch jalan.

19:60 WIB (alias 20:00 kurang 0 detik), site live. WordPress dashboard masuk, TLS hijau (Let’s Encrypt grade A+), homepage default “Hello World” load 0.9 detik, backup cron sudah test-run dan file pertama landed di Backblaze B2 dalam 1.4 detik upload. Total 1 jam 47 menit, 13 menit di bawah target 2 jam.

Influencer-nya drop post 20:00 WIB tepat. Jam 21:30, klien WA lagi:

“Bro, 4.200 visitor di 90 menit terakhir. Server lancar. Gimana caranya?”

Caranya bukan trik, bukan hack, bukan refresh skill setiap kali. Caranya: playbook yang sama saya jalankan ke-12 kali, dengan 9 fase berurutan, estimasi waktu per fase yang sudah saya verifikasi di 12 deploy berbeda. Artikel ini adalah playbook itu, fase per fase, command per command, gotcha per gotcha. Ikuti urut-urutannya, dan Anda akan dapat hasil yang sama: WordPress live, TTFB <200 ms, backup harian, firewall aktif, dalam waktu maksimal 2 jam.

Table of Contents

1. Kenapa 2 Jam, Kenapa Playbook, Kenapa Ubuntu 24.04

Awal 2024 saya audit waktu setup VPS untuk klien. Average-nya 6–8 jam, kadang menyambung ke hari berikutnya. Bukan karena setupnya susah, karena saya ad-hoc: cari command di docs, debug error 502 satu jam, lupa restart fail2ban setelah edit jail, ketemu MariaDB 10.6 yang konflik dengan 10.11, kena rate limit Let’s Encrypt karena lupa pakai --staging saat testing. Setiap setup beda penyebab gagalnya.

Solusinya bukan jadi lebih pintar. Solusinya fixed playbook: 9 fase dengan order yang sudah saya verifikasi tidak punya hidden dependency, command yang sudah saya copy-paste 12 kali tanpa typo, gotcha yang sudah saya cantumkan di checklist supaya tidak lupa. Hasilnya konsisten: 1 jam 47 menit ± 10 menit dari Deploy ke WordPress live.

Kenapa Ubuntu 24.04? Tiga alasan:

  1. LTS sampai 2029, saya tidak mau setup ulang setiap 2 tahun.
  2. Kernel 6.8, support hardware NVMe + io_uring lebih baik (penting untuk MariaDB performance di VPS NVMe).
  3. PHP 8.3 default + Nginx 1.24 sudah termasuk paket bawaan, tidak perlu compile manual.

Kenapa target 2 jam, bukan 4 jam atau 1 jam? Karena 2 jam cukup untuk semua klien yang saya hadapi (small business + early-stage e-commerce + blog niche). Klien bisa block calendar 2 jam, saya kerjakan sekaligus, tidak ada “tunggu besok”. 1 jam terlalu ketat untuk handle DNS propagasi yang tidak bisa saya kontrol. 4 jam terlalu lama untuk klien yang butuh fast turnaround.

Tabel BEFORE vs AFTER: Saya Sebelum & Sesudah Playbook

Metric Ad-hoc (2023) Playbook fixed (sejak Q1 2024)
Total waktu setup 6–8 jam (kadang 2 hari) 1 jam 47 menit (±10 menit)
Debug error tak terduga 2–3 kali per setup < 1 kali, biasanya di gotcha #1–4
Hand-over doc Dirakit ulang per klien 5 template siap copy
TTFB out-of-the-box Tidak konsisten (200–500 ms) 187 ms (Tokyo → Jakarta)
Klien follow-up minggu pertama 3–4 pertanyaan rata-rata 0–1 pertanyaan

2. Pre-requisites Checklist (15 Menit Sebelum Klik Deploy)

Saya tidak mulai stopwatch sampai 8 hal ini sudah ready. Kalau satu missing, setup pasti over-time.

  •  SSH key pair sudah di-generate di laptop lokal (ssh-keygen -t ed25519 -C "ops@larhtech"). Public key copy ke clipboard.
  •  Domain sudah purchased dan Anda punya akses DNS panel (Cloudflare/registrar). Tab DNS panel sudah buka.
  •  Backblaze B2 bucket sudah dibuat dengan nama unik (mis. larhtech-backup-namaklien), Application Key ID + Application Key sudah disimpan di password manager. (Application Key ID bukan Master Key, jangan tertukar.)
  •  VPS provider account ter-topup minimal $20 (kalau Vultr/DO/Hetzner, saya akan jelaskan pilihan di Fase 1).
  •  Password manager terbuka dan siap generate password DB random.
  •  Screen recorder ON (OBS atau QuickTime), untuk dokumentasi handover ke klien. Kalau Anda freelancer, ini wajib.
  •  Terminal split (tmux atau iTerm) siap, 2 pane minimum: pane A untuk SSH, pane B untuk tail -f /var/log/nginx/error.log.
  •  Browser dua tab kosong, satu untuk DNS panel, satu untuk VPS dashboard.

Pro tip 💡 #1 Selalu Pakai tmux di SSH

Sebelum mulai install apa pun, langsung tmux new -s setup. Kalau Wi-Fi Anda putus di tengah apt upgrade, Anda tidak kehilangan progress. Reconnect SSH, jalankan tmux attach -t setup, lanjut dari mana berhenti. Saya pernah kena Wi-Fi mati di tengah Fase 3 untuk klien ke-4. Karena pakai tmux, saya cuma kehilangan 90 detik untuk reconnect, bukan 18 menit rebuild dari awal.

3. Fase 1: Provision VPS (4 menit)

3.1. Pilih Provider

Saya hampir selalu pakai Vultr High Frequency Tokyo untuk klien Indonesia. Alasan: lokasi terdekat, NVMe, harga masuk akal. Tapi ini bukan satu-satunya pilihan.

Provider Spek minimum Harga/bln NVMe IPv6 Lokasi Asia Support
Vultr HF 2 vCPU / 2 GB $12 Ya Ya Tokyo, Singapore Email 4 jam
DigitalOcean 2 vCPU / 2 GB $18 Ya Ya Singapore Email 2 jam
Hetzner CCX 2 vCPU / 4 GB €13.10 Ya Ya Singapore Email 12 jam
Contabo 4 vCPU / 8 GB $7.99 SSD (NVMe paid) Ya Singapore Lambat (24–48 jam)

Untuk klien tipikal Indonesia (blog/UMKM/early e-commerce), Vultr HF Tokyo menang di latency. Jakarta ↔ Tokyo: 60–80 ms RTT. Jakarta ↔ Singapore: 20–40 ms tapi Tokyo NVMe-nya lebih cepat di disk I/O berdasarkan benchmark saya. Untuk yang prioritas IDR-friendly, Hetzner Singapore CCX worth dipertimbangkan.

3.2. Deploy

Klik Deploy New Server → pilih:

  • Cloud Compute → High Frequency
  • Location: Tokyo
  • Image: Ubuntu 24.04 LTS
  • Plan: $12/mo (2 vCPU / 2 GB / 50 GB NVMe)
  • SSH Key: paste public key yang sudah Anda generate di pre-req
  • Hostname: larhtech-namaklien-prod

Klik Deploy. Tunggu IP keluar, biasanya 90–180 detik. Saat IP keluar, langsung:

ssh [email protected]

(203.0.113.42 adalah placeholder IP, ganti dengan IP VPS Anda. Konvensi RFC 5737 untuk dokumentasi.)

Setelah masuk, jalankan update:

apt update && apt upgrade -y
apt install -y unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades

unattended-upgrades auto-patch security updates harian, saya selalu aktifkan ini di fase pertama. Klien tidak akan ingat update server, jadi biar mesin yang update sendiri.

Setup pesanan VPS — pastikan SSH key sudah tercentang sebelum Deploy, kalau lupa nanti harus pakai password yang dikirim email (lama).
Setup pesanan VPS, pastikan SSH key sudah tercentang sebelum Deploy, kalau lupa nanti harus pakai password yang dikirim email (lama).

4. Fase 2: SSH Hardening + Non-Root User (12 menit)

Root login = pintu masuk brute force. Wajib disable hari pertama.

4.1. Buat User Non-Root

adduser larhtechops
usermod -aG sudo larhtechops
mkdir -p /home/larhtechops/.ssh
cp ~/.ssh/authorized_keys /home/larhtechops/.ssh/
chown -R larhtechops:larhtechops /home/larhtechops/.ssh
chmod 700 /home/larhtechops/.ssh
chmod 600 /home/larhtechops/.ssh/authorized_keys

4.2. Edit SSH Config

nano /etc/ssh/sshd_config

Ubah / pastikan baris berikut:

Port 2222
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AllowUsers larhtechops

Catatan ⚠️ #1 JANGAN Restart SSH Sebelum Test Login dari Terminal Kedua

Saya dua kali kena ini di awal karir: restart sshd, koneksi lama tetap hidup, tapi koneksi baru ditolak karena ada typo di config. Akibatnya: VPS terkunci, harus pakai console web provider untuk fix. Buka terminal kedua, login dengan user baru dulu:

ssh -p 2222 [email protected]

Kalau berhasil masuk dan bisa sudo -i, baru lanjut. Kalau gagal, fix dari terminal pertama (yang masih hidup) tanpa panic.

Setelah test login OK, restart SSH dari salah satu sesi:

sudo systemctl restart ssh
sudo systemctl status ssh

Output yang seharusnya:

● ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/usr/lib/systemd/system/ssh.service; enabled; preset: enabled)
     Active: active (running) since Fri 2026-05-22 18:25:14 WIB; 3s ago
   Main PID: 3421 (sshd)
      Tasks: 1 (limit: 2310)
     Memory: 1.4M
        CPU: 11ms

Pro tip 💡 #2 Generate Password DB / User dengan openssl

Jangan ketik random di keyboard. Pakai:

openssl rand -base64 24

Output:

8sX2k+vQzN9TpMx7RfYuH3LmA4WbCe5g

Salin ke password manager. Ini cara saya generate semua password yang berbau credential di setup: DB password, Redis password (di artikel lain), rclone obscure password, dll.

5. Fase 3: LEMP Stack: Nginx + MariaDB 10.11 + PHP 8.3-FPM (18 menit)

Ini fase terpanjang. 3 komponen, 3 sumber repo berbeda. Order penting: MariaDB sebelum PHP-FPM (karena PHP-FPM punya dependency mysql client), Nginx kapan saja (independent).

5.1. Diagram Alur Request Setelah LEMP Aktif

Diagram Alur Request Setelah LEMP Aktif
Diagram Alur Request Setelah LEMP Aktif

5.2. Install Nginx

sudo apt install -y nginx
sudo systemctl enable --now nginx
nginx -v
# nginx version: nginx/1.24.0 (Ubuntu)

5.3. Install MariaDB 10.11 dari Repo Official

Saya selalu pakai repo official mariadb.org, bukan paket bawaan Ubuntu yang masih 10.6.x. Alasan: support window 10.11 LTS sampai Februari 2028, 10.6 sampai Juli 2026.

curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup | sudo bash -s -- --mariadb-server-version=10.11
sudo apt update
sudo apt install -y mariadb-server mariadb-client
sudo mariadb-secure-installation

Saat mariadb-secure-installation jalan:

  • Switch to unix_socket authentication? → n (sudah default di 10.11)
  • Set root password? → n (sudah pakai unix_socket)
  • Remove anonymous users? → Y
  • Disallow root login remotely? → Y
  • Remove test database? → Y
  • Reload privilege tables? → Y

5.4. Install PHP 8.3-FPM dari PPA Sury

PHP 8.3 default Ubuntu 24.04 adalah 8.3.0. Saat artikel ini ditulis sudah ada CVE-2024-2756 dan CVE-2024-3096 yang belum di-patch di repo Ubuntu. PPA ondrej/php maintain versi paling baru (8.3.6+).

sudo apt install -y software-properties-common
sudo add-apt-repository -y ppa:ondrej/php
sudo apt update
sudo apt install -y php8.3-fpm php8.3-mysql php8.3-curl php8.3-xml \
                    php8.3-mbstring php8.3-gd php8.3-zip php8.3-intl \
                    php8.3-bcmath php8.3-opcache
sudo systemctl enable --now php8.3-fpm
php -v
# PHP 8.3.6 (cli) (built: Apr 12 2024 18:11:08) (NTS)

Edit pool config /etc/php/8.3/fpm/pool.d/www.conf:

user = www-data
group = www-data
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6

Edit /etc/php/8.3/fpm/php.ini:

memory_limit = 256M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 120
sudo systemctl restart php8.3-fpm
Screenshot terminal output htop setelah LEMP install selesai
RAM idle setelah Nginx + MariaDB 10.11 + PHP 8.3-FPM jalan: 412 MB dari 2 GB. Sisanya untuk WordPress + cache.

6. Fase 4: WordPress + wp-cli (9 menit)

6.1. Buat Database

sudo mariadb -e "CREATE DATABASE wp_namaklien CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
sudo mariadb -e "CREATE USER 'wp_namaklien'@'localhost' IDENTIFIED BY 'YOUR_DB_PASSWORD';"
sudo mariadb -e "GRANT ALL PRIVILEGES ON wp_namaklien.* TO 'wp_namaklien'@'localhost';"
sudo mariadb -e "FLUSH PRIVILEGES;"

Ganti YOUR_DB_PASSWORD dengan output openssl rand -base64 24 Anda.

6.2. Install wp-cli

curl -O https://raw.githubusercontent.com/wp-cli/wp-cli/v2.10.0/phar/wp-cli.phar
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
wp --info | head -3
# OS:	Linux 6.8.0-31-generic
# Shell:	/bin/bash
# PHP binary:	/usr/bin/php8.3

6.3. Install WordPress

sudo mkdir -p /var/www/situs-klien.com
sudo chown -R larhtechops:www-data /var/www/situs-klien.com
cd /var/www/situs-klien.com

wp core download --locale=en_US
wp config create --dbname=wp_namaklien --dbuser=wp_namaklien --dbpass='YOUR_DB_PASSWORD' --dbhost=localhost
wp core install --url=https://situs-klien.com --title="Situs Klien" \
                --admin_user=admin_klien --admin_password='YOUR_ADMIN_PASSWORD' \
                [email protected]

sudo chown -R www-data:www-data /var/www/situs-klien.com
sudo find /var/www/situs-klien.com -type d -exec chmod 755 {} \;
sudo find /var/www/situs-klien.com -type f -exec chmod 644 {} \;
sudo chmod 600 /var/www/situs-klien.com/wp-config.php

Catatan ⚠️ #2 Install dengan locale en_US Dulu

Saya pernah install langsung --locale=id_ID dan kena bug di install wizard versi 6.5.x, admin user tidak ke-create dengan benar. Aman: install pakai en_US, lalu setelah masuk dashboard, Settings → General → Site Language ganti ke Bahasa Indonesia. WP otomatis download language pack.

6.4. Server Block Nginx

Buat /etc/nginx/sites-available/situs-klien.com:

server {
    listen 80;
    listen [::]:80;
    server_name situs-klien.com www.situs-klien.com;
    root /var/www/situs-klien.com;
    index index.php index.html;

    access_log /var/log/nginx/situs-klien.access.log;
    error_log  /var/log/nginx/situs-klien.error.log;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }

    location ~ /\.ht { deny all; }
    location = /favicon.ico { log_not_found off; access_log off; }
    location = /robots.txt  { log_not_found off; access_log off; allow all; }
}
sudo ln -s /etc/nginx/sites-available/situs-klien.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Screenshot browser dengan halaman default WordPress Hello world! di domain klien
Milestone Fase 4: WordPress live, tapi masih HTTP. Fase 5 selanjutnya untuk TLS.

7. Fase 5: TLS Let’s Encrypt via Certbot (6 menit)

7.1. Point DNS

Sebelum jalankan Certbot, pastikan A record domain sudah point ke IP VPS. Cek dari laptop lokal:

dig +short situs-klien.com
# 203.0.113.42

Kalau hasil masih kosong atau IP lama, jangan lanjut. Tunggu 60–120 detik (Cloudflare biasanya cepat). Kalau >5 menit belum keluar, kemungkinan TTL DNS lama belum expired.

7.2. Install Certbot via Snap

Versi apt install certbot di Ubuntu 24.04 masih 2.x lama. Versi snap paling baru:

sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
certbot --version
# certbot 2.9.0

7.3. Issue Certificate

sudo certbot --nginx -d situs-klien.com -d www.situs-klien.com \
             --email [email protected] --agree-tos --no-eff-email --redirect

Output sukses:

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/situs-klien.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/situs-klien.com/privkey.pem
This certificate expires on 2026-08-20.
Deploying certificate
Successfully deployed certificate for situs-klien.com to /etc/nginx/sites-enabled/situs-klien.com
Successfully deployed certificate for www.situs-klien.com to /etc/nginx/sites-enabled/situs-klien.com
Congratulations! You have successfully enabled HTTPS on https://situs-klien.com

Test auto-renewal:

sudo certbot renew --dry-run

Pro tip 💡 #3 Pakai --staging Saat Develop

Let’s Encrypt punya rate limit 5 cert per domain per minggu. Kalau Anda testing config Nginx berulang dan certbot gagal 6x, Anda kena ban 1 minggu. Saat develop, selalu pakai --staging flag, sertifikat-nya tidak valid di browser, tapi flow-nya identik dengan production. Kalau sudah yakin, baru remove --staging dan run ulang untuk dapat sertifikat real.

# Saat develop / testing
sudo certbot --nginx --staging -d situs-klien.com
# Saat production (sertifikat asli)
sudo certbot --nginx -d situs-klien.com --force-renewal

8. Fase 6: Nginx FastCGI Cache (14 menit)

Ini layer cache yang bikin TTFB turun dari ~600 ms (PHP setiap request) ke ~150 ms (Nginx serve langsung dari disk). Detail lengkap teori dan tuning ada di artikel saya yang lain, di sini saya kasih config minimal yang sudah saya pakai 12 kali.

Edit /etc/nginx/nginx.conf di dalam blok http {}:

fastcgi_cache_path /var/cache/nginx/fastcgi
                   levels=1:2 keys_zone=WP_CACHE:100m
                   max_size=2g inactive=60m use_temp_path=off;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;

Update server block /etc/nginx/sites-available/situs-klien.com:

set $skip_cache 0;
if ($request_method = POST)                       { set $skip_cache 1; }
if ($query_string != "")                          { set $skip_cache 1; }
if ($request_uri ~* "/wp-admin/|/wp-login\.php|/xmlrpc\.php|preview=true") { set $skip_cache 1; }
if ($http_cookie ~* "wordpress_logged_in|wp-postpass") { set $skip_cache 1; }

location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    fastcgi_cache WP_CACHE;
    fastcgi_cache_valid 200 301 302 30d;
    fastcgi_cache_bypass $skip_cache;
    fastcgi_no_cache     $skip_cache;
    add_header X-FastCGI-Cache $upstream_cache_status always;
}
sudo mkdir -p /var/cache/nginx/fastcgi
sudo chown www-data:www-data /var/cache/nginx/fastcgi
sudo nginx -t && sudo systemctl reload nginx

Install plugin Nginx Helper by rtCamp di WP Admin → set Caching Method ke nginx fastcgi_cache, Cache Path ke /var/cache/nginx/fastcgi. Plugin ini auto-purge cache saat update post/comment.

Detail lebih dalam (TTL strategy, hit ratio analysis): Tembus 100% PageSpeed dengan Nginx FastCGI. Setelah FastCGI Cache aktif, lanjut ke optimasi lebih dalam dengan menambahkan Object Cache + OPcache, saya tulis lengkap di Page Cache vs Object Cache vs OPcache – Trio Cache yang Bikin WP Saya 8x Lebih Cepat.

9. Fase 7: UFW + fail2ban (11 menit)

9.1. UFW

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 2222/tcp comment 'SSH custom port'
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'
sudo ufw enable
sudo ufw status verbose

Output:

Status: active
Default: deny (incoming), allow (outgoing), disabled (routed)

To                         Action      From
--                         ------      ----
2222/tcp                   ALLOW IN    Anywhere
80/tcp                     ALLOW IN    Anywhere
443/tcp                    ALLOW IN    Anywhere

9.2. fail2ban

sudo apt install -y fail2ban
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local

Tambahkan / edit:

[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 3
ignoreip = 127.0.0.1/8 ::1 203.0.113.10

[sshd]
enabled = true
port = 2222

[nginx-http-auth]
enabled = true

[nginx-botsearch]
enabled = true

Ganti 203.0.113.10 dengan IP statis kantor/rumah Anda (kalau ada). Restart:

sudo systemctl restart fail2ban
sudo fail2ban-client status sshd

Output:

Status for the jail: sshd
|- Filter
|  |- Currently failed:	0
|  |- Total failed:	14
|  `- File list:	/var/log/auth.log
`- Actions
   |- Currently banned:	2
   |- Total banned:	2
   `- Banned IP list:	45.227.x.x 92.118.x.x

Dua IP sudah ke-banned dalam 11 menit setelah aktif. Bot scanner internet memang non-stop.

10. Fase 8: Backup Otomatis ke Backblaze B2 (17 menit)

Saya pakai Backblaze B2 karena harganya $0.005/GB/bulan (~Rp 80/GB), paling murah dari semua object storage S3-compatible. 30 GB backup = Rp 2.400/bulan. Klien bisa terima ini.

10.1. Install rclone

curl https://rclone.org/install.sh | sudo bash
rclone version
# rclone v1.66.0

10.2. Konfigurasi B2

rclone config

Interactive prompt:

  • n (new remote)
  • name: b2
  • Storage: 5 (Backblaze B2)
  • Application Key ID: YOUR_B2_APP_KEY_ID
  • Application Key: YOUR_B2_APP_KEY
  • (sisanya default)

Test:

rclone lsd b2:
# -1 2026-05-22 18:55:21 -1 larhtech-backup-namaklien

10.3. Backup Script

Buat /usr/local/bin/wp-backup.sh:

#!/bin/bash
set -e

SITE="situs-klien.com"
DB_NAME="wp_namaklien"
WEB_ROOT="/var/www/${SITE}"
BACKUP_DIR="/var/backups/wp"
TIMESTAMP=$(date +%Y%m%d-%H%M)
KEEP_DAYS=7

mkdir -p "${BACKUP_DIR}"

# Dump DB
mysqldump --single-transaction --quick "${DB_NAME}" \
    | gzip > "${BACKUP_DIR}/${SITE}-db-${TIMESTAMP}.sql.gz"

# Archive wp-content
tar -czf "${BACKUP_DIR}/${SITE}-files-${TIMESTAMP}.tar.gz" \
    -C "${WEB_ROOT}" wp-content

# Sync ke B2 (retention 30 hari di B2 lifecycle policy)
rclone copy "${BACKUP_DIR}/" b2:larhtech-backup-namaklien/ \
    --include "${SITE}-*-${TIMESTAMP}*"

# Hapus local lama
find "${BACKUP_DIR}" -name "${SITE}-*" -mtime +${KEEP_DAYS} -delete

echo "[$(date +%F\ %T)] Backup ${SITE} selesai."
sudo chmod +x /usr/local/bin/wp-backup.sh
sudo /usr/local/bin/wp-backup.sh   # test manual

10.4. Cron Daily 03:00 WIB

sudo crontab -e

Tambahkan:

# Pastikan PATH lengkap supaya mysqldump & rclone ketemu
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

0 3 * * * /usr/local/bin/wp-backup.sh >> /var/log/wp-backup.log 2>&1
Screenshot Backblaze B2 dashboard yang menampilkan bucket larhtech-backup-namaklien dengan 2 file
Backup pertama landed di B2 dalam 1.4 detik upload, saya selalu test manual run sebelum percaya cron.

Detail strategi backup multi-layer: Backup Artikel Secara Otomatis Ke Google Drive Hanya Dengan aapanel

11. Fase 9: Smoke Test + Benchmark (13 menit)

11.1. Header Check

curl -I https://situs-klien.com/

Output yang saya cari:

HTTP/2 200
server: nginx/1.24.0 (Ubuntu)
date: Fri, 22 May 2026 19:38:14 GMT
content-type: text/html; charset=UTF-8
strict-transport-security: max-age=15552000; includeSubDomains
x-fastcgi-cache: HIT
content-encoding: gzip

Checkpoint:

  • HTTP/2 200 ✓
  • strict-transport-security present ✓
  • x-fastcgi-cache: HIT (request kedua) ✓
  • content-encoding: gzip ✓

11.2. WordPress Smoke Test

  1. Login admin, buat post “Hello World 1”.
  2. Logout, buka post di incognito → screenshot.
  3. Refresh 5x → cek header X-FastCGI-Cache: HIT di DevTools Network tab.
  4. Install plugin Query Monitor → buka homepage → cek query DB (harusnya rendah karena cache aktif).

11.3. Eksternal Benchmark

  • GTmetrix → server Vancouver + server Hong Kong (3 run masing-masing).
  • PageSpeed Insights → Mobile + Desktop.
  • SSL Labs → target grade A+.

Hasil saya di klien #12 (out-of-the-box, sebelum optimisasi lanjut):

Metric Hasil
TTFB Tokyo → Jakarta 187 ms (rata-rata 3 run)
LCP Twenty Twenty-Four 0.9 s
PSI Mobile 94/100
PSI Desktop 99/100
SSL Labs A+
Disk used 2.3 GB / 50 GB
RAM idle RAM idle
Screenshot GTmetrix waterfall report dari server Hong Kong dengan TTFB 187 ms di-highlight
TTFB 187 ms tanpa Redis Object Cache, tanpa Cloudflare APO. Cuma Nginx FastCGI + OPcache default + NVMe.

Tabel Checklist 9 Fase (Print Ini, Tempel di Sebelah Monitor)

Fase Aktivitas Estimasi Status
1 Provision VPS 4 menit
2 SSH hardening + non-root user 12 menit
3 LEMP stack (Nginx + MariaDB 10.11 + PHP 8.3) 18 menit
4 WordPress + wp-cli 9 menit
5 TLS Let’s Encrypt 6 menit
6 Nginx FastCGI Cache 14 menit
7 UFW + fail2ban 11 menit
8 Backup otomatis ke B2 17 menit
9 Smoke test + benchmark 13 menit
Total fase aktif 104 menit
Buffer + dokumentasi handover 23 menit
Grand total ±1 jam 47 menit

12. Common Gotcha & Quick Fix

Daftar 8 masalah yang saya temui berulang di 12 klien. Kalau Anda kena salah satunya, jangan panik, semua punya fix singkat.

a. MariaDB 10.11 install gagal karena libmariadb3 dari 10.6 sudah ter-install. Fix: sudo apt purge -y mariadb-* libmariadb* dulu, lalu install ulang dari repo mariadb.org.

b. PHP-FPM socket permission error setelah edit pool config. Symptom: Nginx log “connect() to unix:/run/php/php8.3-fpm.sock failed (13: Permission denied)”. Fix: pastikan listen.owner = www-data dan listen.group = www-data. Restart php-fpm + nginx.

c. WP install wizard timeout 504 di langkah wp core install. Fix: naikkan max_execution_time = 120 di php.ini, restart php-fpm. Atau jalankan wp core install via wp-cli langsung (lebih jarang timeout).

d. Certbot gagal: “DNS problem: NXDOMAIN looking up A”. Fix: A record belum propagasi. Cek dig +short domain.com dari laptop. Tunggu 60–300 detik. Jangan retry Certbot lebih dari 4x atau kena rate limit.

e. UFW block fail2ban karena rule order salah. Symptom: fail2ban ban IP, tapi IP tetap bisa connect. Fix: pastikan fail2ban menggunakan action iptables-multiport (default di Ubuntu 24.04), bukan ufw. Atau migrate ke ufw action dengan banaction = ufw di jail.local.

f. FastCGI cache hit-rate 0% padahal config sudah benar. Symptom: setiap request X-FastCGI-Cache: MISS. Fix: cek cookie WP yang dikirim browser. Kalau ada cookie wordpress_test_cookie (saat ada visitor sebelumnya), kondisi $skip_cache Anda mungkin terlalu permisif. Audit blok if ($http_cookie ...).

g. rclone “B2 bucket not found” padahal nama sudah benar. Fix: cek typo. Nama bucket case-sensitive. Test dengan rclone lsd b2: , kalau bucket muncul di list, pakai nama persis dari output itu.

h. Cron backup tidak jalan padahal manual run berhasil. Symptom: log /var/log/wp-backup.log kosong di pagi hari. Fix: cron pakai PATH minimal (/usr/bin:/bin), mysqldump / rclone di /usr/local/bin tidak terbaca. Tambah PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin di atas baris cron.

13. Yang TIDAK Saya Install di Setup Awal Ini

Setup di atas adalah baseline produksi. Ada beberapa hal yang saya sengaja tidak install di 2 jam pertama:

  • Redis Object Cache. Tidak perlu untuk site <500 post tanpa WooCommerce. Tambah nanti kalau query DB jadi bottleneck. Detail kapan butuh & cara setup ada di artikel saya tentang 3-layer cache WordPress.
  • Cloudflare Tunnel / Argo. Bermanfaat untuk hide IP origin + DDoS protection edge, tapi tambah complexity yang tidak semua klien butuh. Saya tambahkan kalau klien minta atau traffic >50k/bulan.
  • Docker. Untuk single-site WordPress, Nginx native lebih cepat (no container overhead) dan lebih mudah di-debug. Docker masuk akal kalau klien punya multi-site staging/prod pipeline.
  • Webmin / cPanel. Over-engineering. Klien LarhTech rata-rata tidak butuh GUI server admin, mereka kelola dari WP dashboard. SSH cukup untuk saya.
  • Maldet / ClamAV. fail2ban + UFW + WordPress core auto-update + plugin minimal sudah cover threat tier yang lazim. Maldet baru saya pasang kalau klien pernah kena malware injection sebelumnya.

14. Handover ke Klien, 5 Dokumen Wajib

Setiap selesai setup, saya kirim 5 file ke klien (zip-encrypted via 7zip dengan password yang dikirim terpisah lewat sinyal):

  1. credentials.txt : IP VPS, SSH port, user, password DB, password admin WP, B2 key, email Let’s Encrypt. Format key-value, jangan paste screenshot.
  2. cron-jobs.md : list semua cron yang jalan: backup harian 03:00, Certbot renew weekly, fail2ban summary. Disebut waktu trigger + lokasi log.
  3. backup-restore-sop.md : SOP restore dalam 5 langkah: download backup terbaru dari B2, decompress di staging server, import SQL, restore wp-content, point DNS sementara. Saya kasih estimasi waktu (30–45 menit) supaya klien tahu kalau ada disaster.
  4. plugin-theme-list.md : daftar plugin & tema yang sudah pre-install (Astra, Nginx Helper, Query Monitor, Wordfence Login Security versi minimal, dll). Kalau klien install plugin baru di luar list ini, saya minta kabari via WA.
  5. uptime-monitor.md : link Uptime Kuma yang saya jalankan di server terpisah, dengan akses read-only untuk klien. Detail setup Uptime Kuma sendiri disini cara install uptime kuma hanya dengan 5 menit.

Saya juga pasang security baseline tambahan yang minimal, misalnya disable XML-RPC untuk cegah brute force amplification. Detail caranya: Cegah Serangan Brute Force dengan Mematikan XML-RPC

15. FAQ

VPS provider mana yang paling worth untuk WordPress Indonesia?

Untuk single-site standar: Vultr HF Tokyo $12/mo (terbaik latency + NVMe). Untuk budget tipis: Hetzner CCX Singapore €13.10/mo (RAM lebih besar). Untuk yang sudah biasa DO: DigitalOcean Premium AMD SGP1 $18/mo. Contabo cuma saya pakai kalau klien benar-benar budget Rp 100k/bulan dan tidak peduli support response 24–48 jam.

Apakah setup ini bisa untuk multi-site WordPress?

Bisa, dengan modifikasi: edit wp-config.php aktifkan WP_ALLOW_MULTISITE, ulang wp core multisite-install, dan setup wildcard subdomain di Nginx + Let's Encrypt wildcard cert (butuh DNS challenge, bukan HTTP-01). Tapi saya jujur — untuk klien yang butuh multi-site, saya pakai setup terpisah per site di VPS yang sama. Lebih mudah maintain.

Berapa lama setup ini bertahan tanpa maintenance?

Dari 12 klien, 11 site masih jalan dengan setup identik setelah 14+ bulan tanpa modifikasi besar. unattended-upgrades handle security patch, Certbot auto-renew TLS, plugin Nginx Helper handle cache invalidation. Yang perlu cek manual: backup integrity sebulan sekali (test restore di staging) + log fail2ban quarterly.

Apakah Ubuntu 24.04 stabil untuk produksi? (vs 22.04 LTS)

Iya. 24.04 release April 2024, sekarang sudah patch level menengah. Saya migrasi dari 22.04 ke 24.04 di September 2024 setelah test 2 bulan di VPS staging. Tidak ada regression major. Kernel 6.8 lebih baik untuk NVMe + PHP 8.3 default. Kalau Anda sangat konservatif, 22.04 LTS support sampai April 2027 — masih aman.

Kalau saya bukan sysadmin pro, berapa lama saya butuh ikutin playbook ini pertama kali?

Realistis: 4–6 jam pertama kali, karena Anda akan: (1) baca command 2x sebelum jalankan, (2) Google error message kalau ketemu gotcha, (3) screenshot setiap milestone untuk catatan. Setelah eksekusi 2–3 kali (di VPS test, jangan klien dulu), Anda akan turun ke 2.5–3 jam. Setelah 5+ kali, di 2 jam range.

Plugin keamanan WP yang perlu setelah setup ini?

Saya pasang Wordfence Login Security (free, lightweight) untuk 2FA admin + rate limit login. Wordfence full (firewall scanner) tidak saya pasang karena tumpang tindih dengan fail2ban + Nginx rules — dan plugin firewall WP bikin TTFB naik 80–120 ms. Kalau klien insist Wordfence full, saya minta downgrade tier scan ke "manual only".

16. Penutup

Klien toko fashion muslim Surabaya yang saya ceritakan di awal, site live 13 menit sebelum deadline. Influencer drop post jam 20:00 tepat. Traffic naik 4.200 visitor di 90 menit pertama, CPU server idle 23% (artinya masih punya 4x headroom). Zero downtime, zero 502, zero panic WA jam 22:00 malam.

Setelah 14 bulan, site itu masih jalan di VPS yang sama. Klien sudah dua kali kirim cashback Lebaran sebagai apresiasi. Saya tidak pernah upgrade VPS-nya, cuma tambah Redis Object Cache 6 bulan lalu setelah traffic harian stabil di 5k visitor. Detail kapan Anda perlu pindah dari setup baseline ini ke 3-layer cache: Cara Install WP di Ubuntu Server

Playbook ini bukan magic. Ini hasil dari 12 kali pengulangan, mencatat setiap gotcha, dan menolak melompati step. Setelah klien ke-12, saya tidak pernah lagi setup VPS dengan asumsi “saya inget kok urutannya”.

Kalau Anda baru pertama kali ikut playbook ini dan ketemu error yang tidak saya cover di Gotcha section, tinggalkan komentar di bawah dengan output error.log Anda. Saya jawab.

Artikel lanjutan yang saya rekomendasikan setelah ini:

Last Updated on Mei 23, 2026 by larhtechBro

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *