Poslite Service โ VM Deploy Runbook
Status: ๐ข DEPLOYED
Owner: Backend Lead
Port: 8096
DB: db_kesles_merchant_poslite (10 tabel: 6 catalog + 4 sales)
Dokumen terkait:
services/poslite_service/README.mddeploy/systemd/poslite-service.serviceservices/whatsapp/deploy-runbook.md(notification/whatsapp/deploy-runbook) โ pola sister service yang ditiruservices/email/deploy-runbook.md(notification/email/deploy-runbook) โ pola sister service
1. VM topologyโ
| Item | Value |
|---|---|
| Host | ptikn3-vm.cluster-vps.dalang.io |
| User / Group | enalfarid / enalfarid |
| Folder root | /home/enalfarid/kesles_merchant/merchant_poslite/ |
| Binary | poslite-service (Linux ELF amd64 stripped) |
| Env file | .env.production (chmod 644, gitignored) |
| Systemd unit | /etc/systemd/system/poslite-service.service |
| Listen port | 127.0.0.1:8096 (loopback, diakses core_api sebagai reverse proxy) |
| Logs | sudo journalctl -u poslite-service [-f] [--since="1 hour ago"] |
Pattern folder + systemd unit identik dengan merchant_whatsapp/ + merchant_email/
supaya operator tidak belajar layout baru per service.
2. Initial deploy (first-time setup)โ
2.1 Build binary di laptopโ
cd ~/Macbook\ pro/development/kesles_merchant/services/poslite_service
# Cross-compile Linux amd64 stripped
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build \
-trimpath \
-ldflags="-s -w" \
-o dist/poslite-service \
./cmd/server
# Verifikasi
ls -lh dist/poslite-service
file dist/poslite-service # โ ELF 64-bit LSB executable, x86-64, statically linked
2.2 Setup database (WAJIB โ DB baru belum ada di VM)โ
Database db_kesles_merchant_poslite harus dibuat terlebih dahulu.
Buat database (jalankan sebagai superuser):
CREATE DATABASE db_kesles_merchant_poslite
OWNER kesles
ENCODING 'UTF8';
Apply init scripts:
DSN="postgres://kesles:<PASSWORD>@10.8.0.1:5432/db_kesles_merchant_poslite?sslmode=disable"
psql "$DSN" -f merchant_database/db_kesles_merchant_poslite/init/001_create_extensions.sql
psql "$DSN" -f merchant_database/db_kesles_merchant_poslite/init/002_create_schemas.sql
Apply migrations (urutan wajib):
psql "$DSN" -f merchant_database/db_kesles_merchant_poslite/migrations/v1/001_schema_migrations_tracker.sql
psql "$DSN" -f merchant_database/db_kesles_merchant_poslite/migrations/v1/002_catalog_tables.sql
psql "$DSN" -f merchant_database/db_kesles_merchant_poslite/migrations/v1/003_sales_tables.sql
Verifikasi:
psql "$DSN" -c "SELECT schema_name FROM information_schema.schemata WHERE schema_name IN ('catalog','sales');"
# Expected: 2 rows โ catalog, sales
psql "$DSN" -c "SELECT tablename FROM pg_tables WHERE schemaname = 'catalog';"
# Expected: outlets, product_categories, products, product_variants, product_stock, stock_movements
2.3 Create folder + env di VMโ
ssh enalfarid@ptikn3-vm.cluster-vps.dalang.io
mkdir -p ~/kesles_merchant/merchant_poslite/
cd ~/kesles_merchant/merchant_poslite/
Upload services/poslite_service/.env.production ke folder ini, lalu:
chmod 644 .env.production
Env wajib di production (kalau POSTGRES_DSN kosong/salah, service panic saat
ping DB di startup dan systemd masuk restart loop):
| Env | Nilai |
|---|---|
APP_ENV=production | static |
APP_PORT=8096 | static |
POSTGRES_DSN | postgres://kesles:...@10.8.0.1:5432/db_kesles_merchant_poslite?sslmode=disable |
JWT_SECRET | sama dengan auth_service + core_api |
JWT_ISSUER=kesles-merchant-auth | static |
INTERNAL_API_KEY | harus identik dengan POSLITE_SERVICE_INTERNAL_KEY di core_api |
CORE_API_URL=http://127.0.0.1:8080 | static |
INTERNAL_CORE_API_KEY | sama dengan INTERNAL_API_KEY di core_api |
INVENTORY_SERVICE_URL=http://127.0.0.1:8084 | static |
INVENTORY_SERVICE_KEY | sama dengan INVENTORY_SERVICE_API_KEY di core_api |
2.4 Upload binaryโ
Upload services/poslite_service/dist/poslite-service ke
~/kesles_merchant/merchant_poslite/, lalu:
chmod 755 ~/kesles_merchant/merchant_poslite/poslite-service
2.5 Install systemd unitโ
Buat /etc/systemd/system/poslite-service.service:
[Unit]
Description=Kesles Poslite Service (katalog produk + POS merchant)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=enalfarid
Group=enalfarid
WorkingDirectory=/home/enalfarid/kesles_merchant/merchant_poslite
EnvironmentFile=/home/enalfarid/kesles_merchant/merchant_poslite/.env.production
ExecStart=/home/enalfarid/kesles_merchant/merchant_poslite/poslite-service
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal
SyslogIdentifier=poslite-service
# Hardening (selaras firebase + whatsapp + email)
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=read-only
PrivateTmp=true
ReadWritePaths=/home/enalfarid/kesles_merchant/merchant_poslite
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable poslite-service
sudo systemctl start poslite-service
sudo systemctl status poslite-service # Expected: active (running)
2.6 Deploy core_api (aktifkan route /merchant/poslite/)โ
Binary core_api perlu di-update karena route /merchant/poslite/ baru ditambahkan.
Binary sudah di-build di merchant_core_api/dist/core-api.
Tambah 2 baris ke /home/enalfarid/kesles_merchant/merchant_core_api/.env.production:
POSLITE_SERVICE_BASE_URL=http://127.0.0.1:8096
POSLITE_SERVICE_INTERNAL_KEY=5998e19e06b76ed318b59c95b1fbfefa763e82a79eb26ceb72c18388895d119c
Upload binary core_api + restart:
sudo systemctl restart core-api
2.7 Smoke test post-deployโ
# 1. Liveness
curl -s http://127.0.0.1:8096/health
# Expected: {"service":"poslite-service","status":"ok"}
# 2. Readiness (DB ping)
curl -s http://127.0.0.1:8096/ready
# Expected: {"status":"ready"}
# 3. Auth wall โ tanpa key โ 401
curl -s http://127.0.0.1:8096/poslite/catalog/categories \
-H "X-Merchant-ID: test"
# Expected: {"error":"unauthorized"} atau 401
# 4. Test via core_api proxy (butuh JWT dari app)
curl -s https://api-merchant.kesles.com/merchant/poslite/catalog/categories \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "X-Merchant-ID: <MERCHANT_UUID>"
# Expected: {"categories":[]}
3. Re-deploy (update binary)โ
# 1. Build di laptop
cd ~/Macbook\ pro/development/kesles_merchant/services/poslite_service
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -trimpath -ldflags="-s -w" -o dist/poslite-service ./cmd/server
# 2. Upload binary via FTP ke merchant_poslite/
# 3. Restart di VM
sudo systemctl restart poslite-service
# 4. Verify (jangan skip โ kalau startup gagal ping DB, systemd restart loop)
sudo systemctl status poslite-service && curl -s http://127.0.0.1:8096/ready
4. Env rotationโ
4.1 Rotate INTERNAL_API_KEY (paired dengan core_api)โ
Service ini + caller core_api harus rotate sekaligus supaya tidak ada window auth mismatch.
# 1. Generate new key
NEW_KEY=$(openssl rand -hex 32)
# 2. Di VM poslite: update .env.production
# โ INTERNAL_API_KEY=<NEW_KEY>
# 3. Di VM core_api: update .env.production
# โ POSLITE_SERVICE_INTERNAL_KEY=<NEW_KEY>
# 4. Restart kedua service hampir bersamaan
sudo systemctl restart poslite-service && sudo systemctl restart core-api
5. Monitoringโ
# Real-time log
sudo journalctl -u poslite-service -f
# Error 24 jam terakhir
sudo journalctl -u poslite-service --since "24 hours ago" | grep '"level":"ERROR"'
# Cek port listening
sudo ss -tlnp | grep :8096
| Event | Threshold | Action |
|---|---|---|
/ready 503 > 1 min | DB unreachable | Cek POSTGRES_DSN + DB host network |
poslite_proxy_upstream_failed di core_api log | Service down | Cek status poslite-service |
bind: address already in use saat start | Port 8096 dipakai proses lain | sudo ss -lntp | grep 8096 |
6. Troubleshootingโ
Service refuse startโ
sudo systemctl status poslite-service
sudo journalctl -u poslite-service --since "5 minutes ago"
| Log | Penyebab | Fix |
|---|---|---|
poslite-service: failed to open DB | POSTGRES_DSN kosong / format salah | edit .env.production, restart |
poslite-service: failed to ping DB | POSTGRES_DSN salah host / DB down | cek DB topology |
bind: address already in use | port 8096 dipakai proses lain | sudo ss -lntp | grep 8096, kill atau ganti port |
core_api return 404 untuk /merchant/poslite/*โ
Binary core_api lama belum punya route. Upload binary baru + restart core_api.
core_api return 502 bad_gatewayโ
poslite-service tidak running. Cek sudo systemctl status poslite-service.
core_api return 503 service_unavailableโ
POSLITE_SERVICE_BASE_URL belum di-set di core_api .env.production. Tambah + restart core_api.
7. Rollbackโ
# Stop service
sudo systemctl stop poslite-service
# Rollback binary ke commit sebelumnya (di laptop)
cd ~/Macbook\ pro/development/kesles_merchant/services/poslite_service
git log --oneline -5
git checkout <commit-hash> -- .
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" \
-o dist/poslite-service ./cmd/server
# Upload + restart
git checkout main -- .
Kalau poslite-service down, core_api route /merchant/poslite/* return 502 bad_gateway
โ tidak mempengaruhi fitur lain (keypad, transfer, profil, dsb).
8. Disaster recoveryโ
Service stateless โ semua state di Postgres. Rebuild scenario:
- Re-provision VM (atau VM baru)
- Setup user
enalfarid+ folder~/kesles_merchant/merchant_poslite/ - Upload binary +
.env.production - Install systemd unit dari ยง2.5
systemctl enable && start- Update core_api
POSLITE_SERVICE_BASE_URLkalau VM pindah host
RTO target: <30 menit (kalau credential + binary tersedia). RPO: 0 (Postgres replicated).
9. Dokumen referensiโ
services/whatsapp/deploy-runbook.md(notification/whatsapp/deploy-runbook) โ pola referensiservices/email/deploy-runbook.md(notification/email/deploy-runbook) โ pola referensideploy/systemd/poslite-service.serviceโ systemd templatemerchant_database/db_kesles_merchant_poslite/โ migrasi DB