> For the complete documentation index, see [llms.txt](https://docs.haikaldev.my.id/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.haikaldev.my.id/tentang/deploy-pterodactyl.md).

# Deploy di Pterodactyl

AI-Haikaru dirancang untuk hidup nyaman di kontainer Pterodactyl — tapi kontainer panel punya batasan yang tidak ada di laptop, dan beberapa keputusan desain bot lahir langsung dari batasan itu. Halaman ini menjelaskan cara deploy **dan** kenapa beberapa hal dibuat seperti itu.

## Ringkasan lingkungan

| Aspek                   | Kondisi khas kontainer Pterodactyl                                     |
| ----------------------- | ---------------------------------------------------------------------- |
| Node.js                 | Image `ghcr.io/parkervcp/yolks:nodejs_20` → **Node 20.x**              |
| Akses root              | ❌ Tidak ada — `useradd`, `chroot`, Docker-in-Docker semuanya mustahil  |
| Compiler (`g++`/`make`) | ❌ Umumnya tidak ada — **paket native harus punya prebuild**            |
| Port                    | Hanya **Allocation** yang terdaftar di panel yang tembus keluar        |
| Disk                    | Terbatas — housekeeping bot (media 30 hari, workspace 30 hari) penting |

## Langkah deploy

{% stepper %}
{% step %}

### Buat server di panel

* Egg: **Node.js generic** (yolks nodejs\_20 atau lebih baru)
* Startup command: `bash run.sh` — script ini memasang yt-dlp otomatis dan menyegarkannya mingguan
* Alokasikan RAM ≥1 GB (Baileys + sharp + agent loop nyaman di sini)
  {% endstep %}

{% step %}

### Clone kode

```bash
git clone https://github.com/AnakTentara/AI-Haikaru.git .
npm install
```

Update berikutnya cukup `git pull` lalu restart dari panel.
{% endstep %}

{% step %}

### Isi `.env`

Sama seperti [Instalasi dan Setup](/tentang/instalasi.md), plus perhatian khusus soal port:

```env
# Dibaca hanya kalau panel TIDAK menyuntikkan SERVER_PORT
# dan config.json belum punya nilainya. Lihat bagian "Resolusi port".
PORT=25538

# Situs workspace di port terpisah
SITE_PORT=1234
PUBLIC_BASE_URL=https://proj.domain-kamu.com
```

{% endstep %}

{% step %}

### Scan QR

Buka `http://<ip-node>:<port-utama>/` di browser → halaman QR muncul selama belum login → scan. Setelah `ready`, halaman itu berubah jadi `200 OK` polos.
{% endstep %}
{% endstepper %}

## Resolusi port

Ini bagian yang paling sering menghasilkan gejala membingungkan, jadi urutannya dijelaskan lengkap.

{% hint style="warning" %}
**Aturan dasar Pterodactyl:** proses di kontainer boleh `listen()` di port mana pun, tapi **hanya port yang dialokasikan panel yang tembus keluar**. `listen(3000)` tanpa alokasi = proses hidup, dari luar tampak mati.
{% endhint %}

Port ditentukan oleh empat sumber, diperiksa berurutan — yang pertama cocok, itu yang dipakai:

```mermaid
flowchart TB
    A["SERVER_PORT<br><i>disuntik panel hosting</i>"] -->|kosong| B["config.json → port<br><i>ditulis dashboard /config</i>"]
    B -->|kosong| C["PORT<br><i>dari .env</i>"]
    C -->|kosong| D["3000<br><i>bawaan</i>"]

    A -->|ada| AW["✅ dipakai — dan DIKUNCI"]
    B -->|ada| BW["✅ dipakai"]
    C -->|ada| CW["✅ dipakai"]
```

| Urutan | Sumber                 | Kapan menang                                                   |
| ------ | ---------------------- | -------------------------------------------------------------- |
| 1      | `SERVER_PORT`          | Selalu, kalau panel menyuntikkannya. **Tidak bisa ditimpa.**   |
| 2      | `config.json` → `port` | Kalau tidak ada panel. Inilah yang ditulis dashboard `/config` |
| 3      | `.env` → `PORT`        | Kalau dua di atas kosong                                       |
| 4      | `3000`                 | Bawaan terakhir                                                |

{% hint style="info" %}
**Kenapa `config.json` di atas `.env`?** Dashboard `/config` menyimpan ke sana. Sebelum v2.1.1 urutannya terbalik — `.env` menang — jadi mengubah port lewat dashboard **tidak pernah berefek**: halaman bilang "tersimpan", bot restart, port tetap yang lama. Gagal diam-diam, persis saat paling dibutuhkan (pindah host).
{% endhint %}

{% hint style="danger" %}
**Kenapa `SERVER_PORT` tetap menang mutlak?** Panel hanya memetakan port yang ia alokasikan sendiri. Kalau `config.json` boleh menimpanya, satu salah ketik membuat bot mendengarkan di port yang tidak dipetakan — dari luar tampak mati, **dan `/config` ikut tak terjangkau**. Terkunci dari bot sendiri, tanpa jalan balik selain panel/SSH.

Komprominya: panel menang, tapi dashboard **mengatakannya terus terang** ("dikunci panel hosting") alih-alih menerima input yang tak akan pernah dipakai.
{% endhint %}

### Mengubah port dari dashboard

Sejak v2.1.1 port, port situs, dan alamat publik bisa diubah tanpa menyentuh berkas:

1. Buka `/config?token=<CONFIG_TOKEN>`
2. Bagian **Host & Jaringan** paling atas
3. Simpan → **restart bot** (port hanya dibaca saat start)

Kalau panel menyuntikkan `SERVER_PORT`, kolom port ditampilkan dengan peringatan bahwa nilainya dikunci — supaya kamu tidak mengira sudah berubah padahal tidak.

## Setup port yang dipakai penulis

| Port  | Fungsi                                           | Diarahkan dari                           |
| ----- | ------------------------------------------------ | ---------------------------------------- |
| 25538 | Bot utama: QR, `/uptime`, `/config`, `/site/...` | —                                        |
| 1234  | **Situs workspace saja** (`SITE_PORT`)           | `proj.haikaldev.my.id` via reverse proxy |
| 25580 | Dicadangkan (rencana web AI)                     | —                                        |

{% hint style="success" %}
**Kenapa situs dipisah ke port sendiri?** Domain situs itu untuk **dibagikan ke siapa pun** — sedangkan app utama membawa `/config` (kredensial) dan halaman QR (login WhatsApp). Kalau domain publik diarahkan ke app utama, dua halaman paling sensitif ikut terbawa ke domain yang link-nya paling sering disebar.

Dengan `SITE_PORT`, yang mendengarkan di port itu adalah **app terpisah yang hanya tahu situs** — di sana `/config` memang tidak ada.
{% endhint %}

Bonus di port situs: alamat bisa tanpa awalan `/site` — `proj.domain.com/masbro/` langsung jalan.

### Cloudflare

Kalau domainnya di-proxy (awan oranye), Cloudflare hanya meneruskan ke port standar. Solusinya salah satu:

* **Origin Rule** — destination port override → 1234
* **Cloudflare Tunnel** di node
* **Reverse proxy** (nginx/caddy) di node yang mendengarkan 443 dan meneruskan ke 1234

SSL mode `Flexible` kalau origin-mu HTTP polos.

## Batasan kontainer yang memengaruhi fitur

### Tidak ada compiler → paket native harus prebuilt

Sudah diverifikasi per paket:

| Paket                | Status di Node 20 linux                                                               |
| -------------------- | ------------------------------------------------------------------------------------- |
| sharp, ffmpeg-static | ✅ prebuild resmi                                                                      |
| better-sqlite3       | ⚠️ **harus versi 12.4.1** — v13 minta Node ≥22                                        |
| node-pty             | ❌ **tidak ada prebuild linux sama sekali** — fitur terminal PTY dibatalkan karena ini |

{% hint style="warning" %}
Pelajarannya kalau mau menambah dependensi: cek dulu `scripts.install` dan aset rilisnya — jangan berasumsi "nanti juga kebuild".
{% endhint %}

### Node 20 vs mesin dev

`--experimental-permission` (pengurung kode workspace) ada di Node 20–22; Node 23+ menamainya `--permission`. Bot **mendeteksi flag-nya saat runtime** — tidak perlu diatur manual. Baris log yang menandakan sehat:

```
[RUNNER] Permission model Node aktif lewat --experimental-permission
```

Kalau yang muncul `Node tanpa permission model`, kode node user hanya dijaga cwd + env bersih — masih terkurung dari kredensial, tapi lapisan runtime-nya hilang. **Laporkan sebagai bug.**

### Restart & rebuild

* `data/` **selamat** dari restart — sesi, riwayat, workspace tetap ada
* Isi `bin/` (yt-dlp) **hilang** saat rebuild — `run.sh` memasangnya lagi otomatis
* `config.json` tidak dilacak git — `run.sh` menyalinnya dari `config.example.json` kalau belum ada

## Checklist setelah deploy

* [ ] Log start bersih, `Own LID session created successfully` muncul
* [ ] `.ping` dibalas
* [ ] `/ws` menampilkan kartu bantuan
* [ ] *"bikin project coba, tulis fizzbuzz, jalanin"* → berjalan, ada hasil
* [ ] Log memuat `[RUNNER] Permission model Node aktif...`
* [ ] *"hostingin"* → link terkirim → **terbuka dari HP dengan data seluler** (bukan cuma dari jaringan yang sama)
* [ ] `https://domain-situs/…` menampilkan CSS & gambar (bukan putih polos)

## Berikutnya

* Masalah umum & solusinya: [Troubleshooting](/referensi/troubleshooting.md)
* Model keamanan lengkap: [Keamanan](/referensi/keamanan.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.haikaldev.my.id/tentang/deploy-pterodactyl.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
