8.8 KiB
Debian 12 App Server Setup
Panduan ini untuk menyiapkan server kosong Debian 12 sebagai app server QRIS Soundbox Platform.
- App domain:
sms.bizone.id - App user:
qrisapp - App directory:
/opt/qris-soundbox - Env file:
/etc/qris-soundbox/qris-soundbox.env - Local app port:
3000 - MQTT broker:
broker.bizone.id
Broker MQTT boleh berada di server lain. Dokumen ini fokus ke server aplikasi.
1. DNS
Pastikan sms.bizone.id sudah mengarah ke public IP server app.
dig +short sms.bizone.id
curl -4 ifconfig.me
2. Base Packages
Jalankan sebagai root atau user sudo.
sudo apt update
sudo apt upgrade -y
sudo apt install -y \
ca-certificates \
curl \
gnupg \
git \
build-essential \
nginx \
certbot \
python3-certbot-nginx \
postgresql \
postgresql-contrib \
ufw
3. Node.js
Gunakan Node.js 22 untuk runtime production.
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs
node -v
npm -v
4. Firewall
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status
Jangan buka port 3000 ke internet. Traffic publik masuk lewat Nginx.
5. App User
Buat user khusus untuk menjalankan service.
sudo adduser --system --group --home /opt/qris-soundbox qrisapp
sudo install -d -o qrisapp -g qrisapp -m 750 /opt/qris-soundbox
sudo install -d -o root -g qrisapp -m 750 /etc/qris-soundbox
sudo install -d -o qrisapp -g qrisapp -m 750 /var/lib/qris-soundbox/exports
Untuk deploy via git/rsync, operator boleh masuk dengan user sudo biasa, lalu copy release ke /opt/qris-soundbox dan set ownership ke qrisapp:qrisapp.
6. PostgreSQL
Buat database dan user production.
sudo -u postgres psql
Di prompt psql:
CREATE USER qris_app WITH PASSWORD '5174e2c2fb3f8424806d1e5a4ca873a3dd33aace06a7b99c49f1bed9d1f42c4a';
CREATE DATABASE qris_soundbox_platform OWNER qris_app;
GRANT ALL PRIVILEGES ON DATABASE qris_soundbox_platform TO qris_app;
\q
7. Deploy Code
Contoh deploy awal via git:
sudo -u qrisapp git clone <repo-url> /opt/qris-soundbox
cd /opt/qris-soundbox
sudo -u qrisapp npm ci
sudo -u qrisapp npm run typecheck
sudo -u qrisapp npm run build
Jika deploy dari artifact, extract/copy artifact ke /opt/qris-soundbox, lalu:
sudo chown -R qrisapp:qrisapp /opt/qris-soundbox
cd /opt/qris-soundbox
sudo -u qrisapp npm ci
sudo -u qrisapp npm run build
Catatan: command npm run typecheck, npm run db:migrate, dan sebagian smoke script membutuhkan dev dependency. Untuk server staging/pilot awal, gunakan npm ci penuh agar semua command operasional tersedia. Service production tetap menjalankan hasil build lewat npm run start:dist.
8. Environment
Buat env production:
sudo nano /etc/qris-soundbox/qris-soundbox.env
Template awal:
NODE_ENV=production
PORT=3000
TRUST_PROXY=true
JSON_BODY_LIMIT=1mb
LOG_FORMAT=json
LOG_LEVEL=info
ADMIN_AUTH_ALLOW_LEGACY_TOKEN=false
ADMIN_DEV_LOGIN_ENABLED=false
ADMIN_SESSION_SECRET=CHANGE_ME_LONG_RANDOM_ADMIN_SESSION_SECRET
ADMIN_SESSION_TTL_SECONDS=28800
MERCHANT_AUTH_ALLOW_LEGACY_TOKEN=false
MERCHANT_DEV_LOGIN_ENABLED=false
MERCHANT_SESSION_SECRET=CHANGE_ME_LONG_RANDOM_MERCHANT_SESSION_SECRET
MERCHANT_SESSION_TTL_SECONDS=28800
DEVICE_AUTH_ALLOW_LEGACY_TOKEN=false
TRACE_HEADER=x-request-id
IDEMPOTENCY_TTL_MS=300000
INTEGRATION_WEBHOOK_SECRET=CHANGE_ME_LONG_RANDOM_WEBHOOK_SECRET
MQTT_PUBLISH_MODE=broker
MQTT_BROKER_URL=mqtts://broker.bizone.id:8883
MQTT_USERNAME=qris-backend
MQTT_PASSWORD=CHANGE_ME_MQTT_BACKEND_PASSWORD
MQTT_CLIENT_ID=qris-platform-backend-prod
MQTT_CONNECT_TIMEOUT_MS=5000
MQTT_SUBSCRIBE_ENABLED=true
MQTT_SUBSCRIBE_TOPICS=devices/+/uplink/#
MQTT_PUBLISH_FORCE_FAIL_ALL=false
MQTT_PUBLISH_FORCE_FAIL_DEVICE_IDS=
MQTT_PUBLISH_DEFAULT_RETRY_INTERVAL_MS=15000
QF100_MQTT_BROKER_HOST=broker.bizone.id
QF100_MQTT_BROKER_PORT=8883
QF100_MQTT_USERNAME=qris-backend
QF100_MQTT_PASSWORD=CHANGE_ME_MQTT_BACKEND_PASSWORD
QF100_MQTT_KEEP_ALIVE_SECONDS=60
DYNAMIC_QR_EXPIRY_SCHEDULER_ENABLED=true
DYNAMIC_QR_EXPIRY_SWEEP_INTERVAL_MS=60000
DYNAMIC_QR_EXPIRY_SWEEP_LIMIT=100
EXPORT_WORKER_ENABLED=true
EXPORT_WORKER_INTERVAL_MS=2000
EXPORT_WORKER_BATCH_SIZE=2
EXPORT_JOB_STALE_RUNNING_MS=900000
EXPORT_SETTLEMENT_ADJUSTMENT_MAX_ROWS=5000
EXPORT_STORAGE_DIR=/var/lib/qris-soundbox/exports
EXPORT_RETENTION_DAYS=30
RATE_LIMIT_ENABLED=true
RATE_LIMIT_LOGIN_WINDOW_MS=60000
RATE_LIMIT_LOGIN_MAX=20
RATE_LIMIT_DEVICE_WINDOW_MS=60000
RATE_LIMIT_DEVICE_MAX=600
RATE_LIMIT_ADMIN_WRITE_WINDOW_MS=60000
RATE_LIMIT_ADMIN_WRITE_MAX=300
FINANCE_PLATFORM_FEE_BPS=70
SETTLEMENT_ADJUSTMENT_REQUIRE_APPROVAL=true
PGHOST=127.0.0.1
PGPORT=5432
PGUSER=qris_app
PGPASSWORD=CHANGE_ME_STRONG_DB_PASSWORD
PGDATABASE=qris_soundbox_platform
Lock permission:
sudo chown root:qrisapp /etc/qris-soundbox/qris-soundbox.env
sudo chmod 640 /etc/qris-soundbox/qris-soundbox.env
9. Database Migration
cd /opt/qris-soundbox
sudo -u qrisapp env $(sudo cat /etc/qris-soundbox/qris-soundbox.env | xargs) npm run db:migrate
Jika env berisi karakter spesial yang membuat xargs bermasalah, gunakan systemd service sementara atau jalankan lewat shell yang melakukan set -a; source ...; set +a.
cd /opt/qris-soundbox
sudo -u qrisapp bash -lc 'set -a; source /etc/qris-soundbox/qris-soundbox.env; set +a; npm run db:migrate'
10. Systemd Service
Buat service:
sudo nano /etc/systemd/system/qris-soundbox.service
Isi:
[Unit]
Description=QRIS Soundbox Platform
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
Type=simple
User=qrisapp
Group=qrisapp
WorkingDirectory=/opt/qris-soundbox
EnvironmentFile=/etc/qris-soundbox/qris-soundbox.env
ExecStart=/usr/bin/npm run start:dist
Restart=always
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ReadWritePaths=/opt/qris-soundbox /var/lib/qris-soundbox
[Install]
WantedBy=multi-user.target
Start service:
sudo systemctl daemon-reload
sudo systemctl enable qris-soundbox
sudo systemctl start qris-soundbox
sudo systemctl status qris-soundbox --no-pager
journalctl -u qris-soundbox -f
11. Nginx
Buat config:
sudo nano /etc/nginx/sites-available/sms.bizone.id
Isi:
server {
listen 80;
listen [::]:80;
server_name sms.bizone.id;
client_max_body_size 1m;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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:
sudo ln -s /etc/nginx/sites-available/sms.bizone.id /etc/nginx/sites-enabled/sms.bizone.id
sudo nginx -t
sudo systemctl reload nginx
12. TLS Certificate
sudo certbot --nginx -d sms.bizone.id
sudo certbot renew --dry-run
13. Create Production Users
cd /opt/qris-soundbox
sudo -u qrisapp bash -lc 'set -a; source /etc/qris-soundbox/qris-soundbox.env; set +a; npm run admin:create-user -- --email <email> --name <name> --role admin --password <strong-password>'
sudo -u qrisapp bash -lc 'set -a; source /etc/qris-soundbox/qris-soundbox.env; set +a; npm run merchant:create-user -- --merchant <merchant-id-or-code> --email <email> --name <name> --role owner --password <strong-password>'
14. Verification
curl -fsS https://sms.bizone.id/health
curl -fsS https://sms.bizone.id/health/deep
curl -I https://sms.bizone.id/ui
Run production preflight from server:
cd /opt/qris-soundbox
sudo -u qrisapp bash -lc 'set -a; source /etc/qris-soundbox/qris-soundbox.env; set +a; npm run deploy:check-env'
sudo -u qrisapp bash -lc 'set -a; source /etc/qris-soundbox/qris-soundbox.env; set +a; npm run smoke:mqtt-real'
Run full smoke only on staging/controlled environment because it creates and cleans test data:
sudo -u qrisapp bash -lc 'set -a; source /etc/qris-soundbox/qris-soundbox.env; set +a; npm run smoke:e2e'
sudo -u qrisapp bash -lc 'set -a; source /etc/qris-soundbox/qris-soundbox.env; set +a; npm run ui:qa'
15. QF100 Notes
For QF100 devices, app config server URL should point to:
https://sms.bizone.id/speaker/dev-config
The config response will point the device to MQTT broker:
broker.bizone.id:8883
If the firmware still has MQTT TLS disabled, either patch firmware TLS or prepare a restricted non-TLS pilot listener. Do not expose unrestricted non-TLS MQTT to the internet.
16. Routine Ops
Useful commands:
sudo systemctl status qris-soundbox --no-pager
journalctl -u qris-soundbox -n 200 --no-pager
sudo nginx -t
sudo systemctl reload nginx
Backup:
cd /opt/qris-soundbox
sudo -u qrisapp bash -lc 'set -a; source /etc/qris-soundbox/qris-soundbox.env; set +a; npm run backup:production -- --out /var/backups/qris --include-mosquitto'