Files
Qris-Soundbox/DEBIAN12_APP_SERVER_SETUP.md
2026-06-04 11:20:16 +07:00

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'