Dokumentasi pengembang
Bahasa Indonesia

Menjalankan server

Instalasi server

Cara memasang dan menjalankan server Pabal dengan Docker Compose di satu server yang terbuka ke internet.

Dokumen ini menjelaskan cara memasang server Pabal di satu server yang terbuka ke internet. Dengan Docker Compose, Anda menjalankan tiga kontainer — server Pabal · PostgreSQL · Caddy (HTTPS) — dan semua data yang tidak boleh hilang di-bind ke direktori host. Cukup ikuti langkah-langkahnya secara berurutan dari atas.

Nilai contoh dalam dokumen ini

Domain pabal.me, IP publik server 203.0.113.10 (alamat untuk contoh — ganti dengan IP Anda yang sebenarnya saat membaca), lokasi kode sumber /opt/pabal, lokasi data /srv/pabal, sistem operasi Ubuntu 24.04 LTS. Berkas yang diperlukan ada di folder deploy/ dalam repositori.

Gambaran sekilas

Internet docker compose · ssemiya-net /srv/pabal (bind mount) Aplikasi PabalPabal.app Peramban · botsitus · Bot API OperatorSSH pabal-server MTProto :8443 web · Bot API · admin :8080 uid 1000 · JRE 21 webhook → bot (HTTPS) pabal-caddy:80 · :443 TLS otomatis pabal-postgres 16internal saja · tanpa port pabal_server/keys · kunci RSA pabal_server/data · foto pabal_server/logs postgres_data/ · obrolan caddy/ · sertifikat backups/ · backup.sh MTProto :8443 (enkripsi sendiri) HTTPS :443 tunnel SSH → 127.0.0.1:8080/admin/
Satu server, tiga kontainer, dan semua yang harus bertahan ada di /srv/pabal

Penjelasan diagram

  • Tiga area: pengguna di internet → tiga kontainer di server produksi → direktori bind di disk host. Kontainer bisa dihapus dan dibuat ulang kapan saja; semua yang harus bertahan ada di /srv/pabal.
  • Hanya ada dua pintu masuk: aplikasi langsung tersambung ke server lewat port MTProto (terenkripsi sendiri, jadi tidak perlu HTTPS), sedangkan situs web, dokumentasi, dan Bot API melewati port 443 milik Caddy. Caddy memblokir jalur administrasi (/admin, /health …) dan hanya meneruskan sisanya.
  • Garis putus-putus merah adalah jalur khusus operator. Halaman admin hanya terbuka di 127.0.0.1:8080 milik server, sehingga hanya bisa dimasuki lewat tunnel SSH. PostgreSQL sama sekali tidak punya port yang terbuka ke luar.
  • Dua kotak merah adalah data yang paling penting. Kunci RSA di pabal_server/keys ditanam ke dalam aplikasi saat build, jadi jika hilang Anda harus mendistribusikan ulang semua aplikasi; postgres_data/ berisi semua akun dan obrolan. Keduanya prioritas pertama untuk dicadangkan.
  • Panah abu-abu adalah penyimpanan: server menulis kunci, foto, pengaturan, dan log; PostgreSQL menulis event; dan Caddy menulis sertifikat, masing-masing ke direktorinya sendiri.

Persiapan

ButirIsi
ServerUbuntu 24.04 LTS, untuk permulaan 2 vCPU · 4 GB RAM · 40 GB SSD. Jika pengguna dan pesan bertambah, tambahkan memori terlebih dahulu
IPv4 publikIP statis. Aplikasi dibangun dengan alamat server berupa IP, dan server juga memberi tahu aplikasi alamatnya dalam bentuk IP
DomainSatu domain yang DNS-nya bisa Anda ubah (record A)
Sarana pengiriman kode pendaftaranSatu akun SMS (Twilio · Solapi · webhook) atau email (SMTP)
MacMac untuk membangun aplikasi (Pabal.app) bagi server ini

Port

PortSiapaDibuka untukDeskripsi
22/tcpOperatorDisarankan hanya IP operatorSSH
80/tcpCaddySemuaPenerbitan sertifikat · pengalihan ke HTTPS
443/tcp, 443/udpCaddySemuaSitus web · dokumentasi · Bot API (udp untuk HTTP/3)
8443/tcpServerSemuaKoneksi aplikasi (MTProto)
8080/tcpServerJangan dibukaHalaman admin · pemeriksaan kesehatan — hanya di 127.0.0.1 milik server
5432/tcpPostgreSQLJangan dibukaHanya di jaringan internal kontainer
Mengapa port aplikasi bukan 443

Port 443 dipakai oleh situs web (HTTPS). MTProto bukan HTTP dan bukan TLS, jadi keduanya tidak bisa dibagi dalam satu port. Jika Anda ingin aplikasi juga diterima di 443 karena jaringan kantor atau kampus yang memblokir port tak lazim, ambil satu IP lagi dan berikan port 443 di IP itu kepada server (di .env, isi PUBLIC_IP dengan IP kedua dan MTPROTO_PORT=443, lalu bedakan dengan menambahkan IP pada port di berkas compose). Server memberi tahu aplikasi "sambungkan ke port ini di IP ini", jadi port aplikasi di dalam dan di luar kontainer dibuat sama.

1. Menyiapkan server

sudo apt update && sudo apt -y upgrade
timedatectl                      # pastikan tertulis "System clock synchronized: yes"

Sinkronisasi waktu wajib. Nomor pesan MTProto diturunkan dari waktu, jadi jika jam server meleset, aplikasi akan terus mengulang koneksi. Jika no, jalankan sudo timedatectl set-ntp true.

# firewall
sudo ufw allow OpenSSH           # jika memungkinkan: sudo ufw allow from <IP_operator> to any port 22
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 443/udp
sudo ufw allow 8443/tcp
sudo ufw enable

# Docker (skrip instalasi resmi)
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER     # setelah login ulang, docker bisa dipakai tanpa sudo
docker compose version            # v2 atau lebih baru
Docker dan ufw

Port yang dibuka Docker (ports:) terbuka tanpa memedulikan aturan ufw. Karena itu berkas compose hanya membuka port publik (80, 443, 8443), membuka port admin hanya sebagai 127.0.0.1:8080, dan tidak membuka PostgreSQL. Ingat hal ini saat mengubah ports:.

2. Mengambil kode sumber

sudo mkdir -p /opt/pabal && sudo chown $USER: /opt/pabal
git clone <alamat_repositori> /opt/pabal

Jika Anda menyalinnya dari komputer pengembangan, jangan ikut kirim kunci, data, dan log (rsync --exclude 'keys/' --exclude 'data/' --exclude 'logs/' --exclude '**/target/'). Kunci RSA server produksi dibuat baru oleh server produksi saat pertama kali menyala — jika kunci pengembangan dipakai di produksi, komputer pengembangan itu menjadi tempat yang bisa membuka lalu lintas produksi.

3. Direktori data (bind mount)

sudo mkdir -p /srv/pabal/{postgres_data,pabal_server/keys,pabal_server/data,pabal_server/logs,caddy/data,caddy/config,backups}
sudo chown -R 1000:1000 /srv/pabal/pabal_server        # kontainer server berjalan sebagai uid 1000
sudo chown -R $USER: /srv/pabal/backups
sudo chmod 700 /srv/pabal/pabal_server/keys /srv/pabal/backups
DirektoriDi dalam kontainerIsinyaJika hilang
pabal_server/keys/app/keysprivate.pem (kunci privat RSA server), private.pem.pubSemua aplikasi harus dibangun dan didistribusikan ulang
pabal_server/data/app/dataFoto (media/), admin-token, operations.json (pengaturan kode pendaftaran, SMS, dan SMTP, termasuk nilai rahasia)Foto dan pengaturan operasional
pabal_server/logs/app/logsLog server (dikompres setiap hari)Hanya catatan
postgres_data/var/lib/postgresql/dataSemua akun, obrolan, pesan, status masuk, bot, dan pengaturan webhookSeluruh layanan
caddy/data, caddy/config/data, /configSertifikat HTTPSDiterbitkan ulang
backups(khusus host)Hasil backup.sh

4. Berkas pengaturan .env

cd /opt/pabal/deploy
cp .env.example .env
chmod 600 .env
openssl rand -base64 30 | tr -d '/+=' | cut -c1-32     # masukkan hasilnya ke POSTGRES_PASSWORD
nano .env
DOMAIN=pabal.me
PUBLIC_IP=203.0.113.10
POSTGRES_PASSWORD=(nilai yang dibuat di atas)
PABAL_HOME=/srv/pabal
PABAL_SOURCE=..
PABAL_NETWORK=ssemiya-net
MTPROTO_PORT=8443
HTTP_PORT=80
HTTPS_PORT=443
ADMIN_PORT=8080
ADMIN_TOKEN=
JAVA_OPTS="-Xms512m -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=100"
NilaiYang diisiCatatan
DOMAINDomain situs webTautan yang dibuat aplikasi (pabal.me/nama_pengguna), alamat di dokumentasi, dan pratinjau tautan juga memakai alamat ini
PUBLIC_IPIPv4 publik serverDi cloud, IP publik yang tampil di konsol (bukan IP privat di dalam server)
POSTGRES_PASSWORDNilai acakTentukan sebelum menyalakan pertama kali. PostgreSQL hanya memakai nilai ini saat pertama kali membuat direktori data
PABAL_HOME/srv/pabalDirektori dari langkah 3
PABAL_SOURCE..Repositori tempat image server dibangun: .. jika file compose tetap di deploy/ dalam repositori; jika tidak, path ke kode sumber
PABAL_NETWORKssemiya-netJaringan Docker tempat ketiga kontainer bergabung. Buat terlebih dahulu (langkah 6)
MTPROTO_PORT8443Jika diubah, ubah juga firewall dan build aplikasi
ADMIN_TOKENBiarkan kosongJika kosong, token dibuat di pabal_server/data/admin-token saat pertama kali menyala. Jika ingin menentukannya sendiri, minimal 16 karakter
JAVA_OPTSNilai bawaanJika memori kurang, naikkan -Xmx (hingga sekitar setengah memori server)

Berkas compose produksi menyalakan server dengan nomor uji dimatikan (TELEGRAM_TEST_NUMBERS=false). Daftar lengkap nilai pengaturan yang dibaca server ada di Administrasi dan pengaturan — variabel lingkungan.

5. DNS

NamaJenisNilai
pabal.meA203.0.113.10
www.pabal.meA203.0.113.10
dig +short pabal.me        # harus muncul 203.0.113.10 (penerapannya butuh beberapa menit hingga beberapa jam)

Server boleh dinyalakan sebelum DNS mengarah ke server. Caddy akan terus mencoba ulang sampai berhasil mendapatkan sertifikat.

6. Build dan nyalakan

cd /opt/pabal/deploy
docker network inspect ssemiya-net >/dev/null 2>&1 || docker network create ssemiya-net   # cukup sekali
docker compose up -d --build       # pertama kali butuh beberapa menit (Maven mengunduh pustaka)
docker compose ps                  # pabal-server, pabal-postgres, pabal-caddy berstatus running (healthy)
docker compose logs -f pabal-server      # keluar dengan Ctrl+C

Saat pertama kali menyala, baris-baris seperti ini muncul di log.

WARN  ServerKeys - Generated a new RSA key at /app/keys/private.pem (fingerprint -3898654385185406269). Clients must embed /app/keys/private.pem.pub
INFO  TelegramServer - JDBC persistence active (jdbc:postgresql://pabal-postgres:5432/pabal)
INFO  BotFather - BotFather is user 100000
INFO  AdminToken - Created the admin token in /app/data/admin-token
INFO  TelegramServer - Website: http://0.0.0.0:8080/ (published at https://pabal.me/)
  • Baris pertama berupa WARN, tetapi itu normal (pemberitahuan bahwa kunci dibuat hanya sekali di awal). Selanjutnya yang muncul adalah Loaded RSA key … (fingerprint …). Fingerprint berbeda untuk setiap server.
  • Tabel dibuat sendiri oleh server saat menyala.

7. Pemeriksaan

# dari luar (komputer Anda)
curl -sI https://pabal.me/ | head -1                              # HTTP/2 200
curl -s -o /dev/null -w '%{http_code}\n' https://pabal.me/docs/    # 200 — dokumentasi ini
curl -s -o /dev/null -w '%{http_code}\n' https://pabal.me/admin/   # 404 — halaman admin tidak dibuka untuk publik
curl -s https://pabal.me/docs/server-key.pem | head -1             # -----BEGIN RSA PUBLIC KEY-----
nc -vz 203.0.113.10 8443                                          # succeeded — port aplikasi

# di server
curl -s http://127.0.0.1:8080/health          # {"status":"UP",…}
ls -l /srv/pabal/pabal_server/keys                  # private.pem(600), private.pem.pub

8. Halaman admin dan pengaturan pertama

# di komputer Anda (biarkan tetap berjalan)
ssh -N -L 8080:127.0.0.1:8080 <pengguna>@203.0.113.10

# token diambil di server
sudo cat /srv/pabal/pabal_server/data/admin-token

Buka http://localhost:8080/admin/ di peramban dan masukkan token. Pengaturan yang wajib dilakukan sebelum beroperasi (rinciannya di Administrasi dan pengaturan):

  1. Atur Cara pengiriman kode (코드 전달 방식) ke SMS atau email. Dengan cara "Halaman admin" (관리 화면), operator harus memberitahukan kode satu per satu.
  2. Isi nilai SMS atau Email (SMTP) (이메일 (SMTP)) lalu simpan → pastikan kode benar-benar terkirim dengan Kirim uji (테스트 발송).
  3. Pastikan Nomor uji (테스트 번호) dimatikan. Jika dinyalakan, siapa pun bisa masuk dengan nomor +99966….
  4. Jika perlu, matikan Izinkan pendaftaran baru (새 가입 허용) di awal, lalu terima dulu hanya orang yang Anda undang.

9. Menghubungkan aplikasi ke server ini

Aplikasi dibangun dengan IP, port, dan kunci publik server tertanam di dalamnya. Aplikasi hanya bisa tersambung ke server ini jika dibangun ulang dengan kunci publik server ini. Di Mac:

# 1. kunci publik server ini (kunci publik, bukan kunci privat — boleh juga diunduh dari situs dokumentasi)
curl -s -o keys/production.pem.pub https://pabal.me/docs/server-key.pem

# 2. arahkan kode sumber aplikasi ke server ini + terapkan branding
python3 scripts/tdesktop/point_to_server.py ~/Developer/tdesktop \
    --host 203.0.113.10 --port 8443 --key keys/production.pem.pub
python3 scripts/tdesktop/apply_branding.py ~/Developer/tdesktop

Setelah itu, jalankan langkah 5 (configure) dan langkah 6 (build) di docs/tdesktop-build-guide.md dalam repositori. Salin hasilnya, out/Debug/Pabal.app, dengan nama lain — jika kode sumber yang sama dibangun ulang untuk server lain, hasilnya akan tertimpa.

Sebelum membagikan aplikasi kepada orang lain

Build saat ini adalah build debug tanpa tanda tangan dan notarisasi, jadi penerima harus membukanya dengan klik kanan → Buka saat pertama kali. Build debug menyimpan data di folder di samping aplikasi (tdata/), dan jika tidak bisa menulis di sana, ia memakai folder data yang sama dengan Telegram Desktop asli. Arahkan pengguna untuk menaruh aplikasi di folder pengguna (misalnya ~/Applications/Pabal/). Build rilis untuk distribusi umum (folder data terpisah, tanda tangan dan notarisasi Apple) adalah pekerjaan tersendiri.

Operasional

Status dan log

cd /opt/pabal/deploy
docker compose ps
docker compose logs --since 1h pabal-server
tail -f /srv/pabal/pabal_server/logs/telegram-server.log

Pembaruan

cd /opt/pabal && git pull
cd deploy
./backup.sh                                     # cadangkan dulu
docker compose up -d --build pabal-server             # ganti server saja dengan image baru
docker compose logs --since 5m pabal-server | grep -E "Website|ERROR"

Selama beberapa puluh detik server menyala ulang, aplikasi terputus lalu tersambung kembali dengan sendirinya tanpa perlu masuk lagi. Perubahan tabel diterapkan otomatis saat server menyala. Direktori bind tidak bergantung pada image, jadi data tidak terhapus oleh docker compose down atau penghapusan image — asalkan Anda tidak menghapus /srv/pabal.

Pencadangan

./backup.sh            # → /srv/pabal/backups/<tanggal-waktu>/{pabal.dump, server-keys-data.tar.gz}
crontab -e             # setiap hari pukul 03:00:
# 0 3 * * * /opt/pabal/deploy/backup.sh >> /srv/pabal/backups/backup.log 2>&1
  • Cadangan yang berumur lebih dari 14 hari dihapus otomatis (bisa diubah, misalnya KEEP_DAYS=30 ./backup.sh).
  • Salin juga ke tempat lain. Cadangan di disk yang sama tidak selamat jika disk itu rusak.
  • Cadangan berisi kunci privat RSA serta nilai rahasia SMS dan SMTP. Jaga tempat penyimpanannya seketat server itu sendiri.

Pemulihan

cd /opt/pabal/deploy
B=/srv/pabal/backups/20260920-030000            # cadangan yang akan dipulihkan
docker compose stop pabal-server pabal-caddy
docker compose exec -T pabal-postgres dropdb -U pabal pabal
docker compose exec -T pabal-postgres createdb -U pabal pabal
docker compose exec -T pabal-postgres pg_restore -U pabal -d pabal --no-owner < $B/pabal.dump
sudo tar -C /srv/pabal/pabal_server -xzf $B/server-keys-data.tar.gz
sudo chown -R 1000:1000 /srv/pabal/pabal_server
docker compose up -d

Hal yang sama berlaku saat pindah ke server baru: lakukan langkah 1–5, lalu jalankan pemulihan di atas sebagai ganti langkah 6, dan nyalakan. Jika kunci yang sama dipulihkan, aplikasi tidak perlu dibangun ulang (kecuali IP server berubah).

Menyalakan dan mematikan

docker compose restart pabal-server       # nyalakan ulang server saja
docker compose stop                 # hentikan semuanya (data tetap utuh)
docker compose up -d                # nyalakan lagi

Kontainer memakai restart: unless-stopped, jadi menyala sendiri meskipun server di-reboot.

Daftar periksa keamanan

  • Izin .env 600, POSTGRES_PASSWORD berupa nilai acak
  • Izin /srv/pabal/pabal_server/keys 700, dan kunci privat tidak ada di tempat lain selain cadangan
  • Firewall: hanya 22 (sebisa mungkin hanya IP operator), 80, 443, dan 8443
  • https://domain/admin/ menghasilkan 404 dari luar
  • Halaman admin: Nomor uji (테스트 번호) mati, Cara pengiriman kode (코드 전달 방식) SMS/email, Kirim uji (테스트 발송) berhasil
  • TELEGRAM_WEBHOOK_ALLOW_LOCAL tidak dinyalakan (agar webhook bot tidak bisa menuju jaringan internal)
  • SSH: matikan login dengan kata sandi, hanya dengan kunci
  • Cron cadangan dan salinan eksternal sudah diperiksa, dan pemulihan sudah dilatih sekali
  • Pembaruan keamanan otomatis OS server (unattended-upgrades)

Pemecahan masalah

GejalaPenyebab → solusi
Aplikasi tidak pernah lewat dari "Connecting…"① 8443 terblokir → nc -vz IP 8443, periksa ufw dan security group cloud ② aplikasi dibangun dengan kunci publik lain → ulangi langkah 9 dengan kunci server ini ③ PUBLIC_IP salah → perbaiki .env lalu docker compose up -d pabal-server
Awalnya tersambung, tetapi segera terputusAplikasi berpindah ke alamat yang diberitahukan server (PUBLIC_IP:MTPROTO_PORT), tetapi alamat itu salah → periksa PUBLIC_IP
Error sertifikat HTTPSDNS belum mengarah ke server ini, atau port 80 terblokir → dig +short domain, docker compose logs pabal-caddy
server terus menyala ulang, password authentication failedPOSTGRES_PASSWORD diubah setelah data dibuat → kembalikan ke nilai semula, atau jalankan ALTER USER pabal PASSWORD '…' di dalam DB
AccessDeniedException: /app/keys/…Pemilik direktori bind → sudo chown -R 1000:1000 /srv/pabal/pabal_server
OutOfMemoryError, menjadi lambatNaikkan -Xmx di JAVA_OPTS lalu docker compose up -d pabal-server
Kode pendaftaran tidak datangLihat status pengiriman dan alasan kegagalan di tab Kode pendaftaran (가입 코드) pada halaman admin → periksa nilai SMS dan SMTP di tab Pengaturan (설정), lalu Kirim uji (테스트 발송)
Tidak bisa masuk ke halaman adminPastikan tunnel SSH berjalan, dan periksa apakah port 8080 di komputer Anda dipakai program lain (ganti dengan -L 18080:127.0.0.1:8080 lalu buka localhost:18080)
network ssemiya-net declared as external, but could not be foundJaringan belum dibuat → docker network create ssemiya-net (atau nama lain lewat PABAL_NETWORK di .env)

Keterbatasan yang diketahui

  • Ini susunan satu server. Saat menyala, server memuat semua akun, obrolan, dan pesan ke memori. Penskalaan horizontal ke beberapa server belum bisa, dan jika data bertambah, memori harus ditambah.
  • Kode pendaftaran yang sedang menunggu dan update bot yang belum diambil ada di memori, jadi hilang saat server dimulai ulang.
  • Permintaan kode pendaftaran punya jeda dan batas harian, tetapi belum ada pembatasan laju untuk permintaan lainnya secara keseluruhan. Sesaat setelah dibuka untuk publik, sering-seringlah memeriksa log dan dasbor.
  • Belum ada aplikasi ponsel dan notifikasi push. Lampiran pesan hanya foto. Kanal, supergrup, dan verifikasi dua langkah belum tersedia.
  • Auth key tersimpan di DB sebagai teks biasa (diperlukan agar server bisa mendekripsi). Jaga DB dan cadangannya seketat kunci RSA.

Berkas

BerkasPeran
deploy/docker-compose.ymlSusunan produksi (pabal-server · pabal-postgres · pabal-caddy, bind mount)
deploy/CaddyfileHTTPS, pemblokiran jalur admin, penerusan situs, dokumentasi, dan Bot API
deploy/.env.exampleContoh pengaturan → deploy/.env
deploy/backup.shDump DB + arsip kunci dan data, pembersihan cadangan lama
DockerfileImage server (build → JRE 21, uid 1000)