Self-Hosting n8n dan Flowise di Platform Bertagihan Rupiah

Foto oleh Mhzk1600 via Wikimedia Commons (CC BY-SA 3.0)
Karena container-nya tidak punya persistent volume. n8n menyimpan workflow, credential, dan riwayat execution di database SQLite bawaan, dan di platform container runtime storage bersifat ephemeral secara default, sehingga file itu hidup dan mati bersama container. Pasang volume dengan mount di direktori data n8n, dan redeploy yang sama jadi tidak berbahaya.
Untuk n8n path-nya /home/node/.n8n, tempat user node di dalam image menyimpan database SQLite, file settings, dan log instance. Compose file resmi Flowise memasang folder .flowise di bawah /home/node dengan alasan yang sama, karena container itu juga berjalan sebagai user node non-root. Panduan lama yang menunjukkan path di bawah /root dibuat sebelum perubahan tersebut dan memasang direktori yang tidak pernah ditulis prosesnya.
n8n menyusun webhook URL dari N8N_PROTOCOL, N8N_HOST dan N8N_PORT, yang default-nya http, localhost, dan 5678. Di belakang reverse proxy, dan setiap managed platform adalah reverse proxy, nilai-nilai itu menggambarkan bagian dalam container, bukan alamat publiknya. Setel N8N_WEBHOOK_URL ke base URL Anda yang sebenarnya dan N8N_PROXY_HOPS ke jumlah proxy yang ada di depannya.
Anda hanya bisa memberikan kunci sendiri selama kunci itu belum masuk ke file settings. Setelah n8n membuat kuncinya sendiri saat launch pertama, mengarahkan variabel itu ke nilai lain tidak mengenkripsi ulang apa pun dan justru membuat credential yang tersimpan tidak terbaca. Mengganti kunci yang sudah dipakai adalah fitur rotasi opt-in terpisah yang dokumentasinya meminta Anda mengambil full backup database lebih dulu.
Kedua project tidak menerbitkan angka memori minimum, jadi anggap ini bergantung pada beban kerja, bukan angka tetap. Dokumentasi n8n mencatat bahwa ia tidak membatasi berapa banyak data yang boleh diambil dan diproses sebuah node, dan bahwa node Code, binary payload besar, serta manual execution semuanya menggelembungkan satu run. Jalankan kedua service secara terpisah, pecah job besar, dan awasi baris JavaScript heap out of memory di log.

Foto oleh Mhzk1600 via Wikimedia Commons (CC BY-SA 3.0)
Ringkasan Utama
Deploy sekali klik untuk n8n atau Flowise hanya memberi Anda satu container yang berjalan dan satu URL publik, tidak lebih. Anda sendiri yang memasang persistent volume, mengatur alamat webhook publik, memaku encryption key sebelum boot pertama, dan memasang authentication di depan editor. Lewatkan satu saja, dan redeploy berikutnya menghapus credential Anda tanpa suara.
Daftar template sekali klik di sebuah PaaS Indonesia terbaca seperti daftar keinginan solo builder: Supabase, PostgreSQL, Redis, Uptime Kuma, lalu dua entri yang paling menarik bagi saya, n8n untuk workflow automation dan Flowise untuk membangun LLM flow. Ditagih dalam rupiah, dibayar lewat QRIS atau transfer bank, tanpa kartu kredit sama sekali. Sekali klik, keduanya sudah menjawab di sebuah subdomain dengan TLS yang sudah beres.
Klik itu memang bagian yang mudah, dan bukan bagian yang menentukan apakah instance-nya masih ada bulan depan. Tulisan ini membahas empat hal yang tidak dikerjakan tombol itu untuk Anda, dengan setiap nama environment variable saya cek ke dokumentasi resmi n8n dan Flowise, bukan ke tutorial: persistent volume, URL publik, encryption key, dan authentication.
Mulailah dari kalimat platform-nya sendiri, karena di situ seluruh masalahnya: secara default sebuah service mendapat runtime storage yang ephemeral, dan apa pun yang harus selamat dari redeployment butuh persistent volume yang dipasang. n8n dan Flowise dua-duanya berada di sisi yang salah dari default itu. Keduanya tidak datang dengan database service terpisah, dan keduanya menulis state ke sebuah direktori di dalam container seolah-olah direktori itu masih akan ada besok.
# Every piece of durable n8n state lives in one directory, and the image runs
# as the "node" user, so that directory is /home/node/.n8n — not /root/.n8n.
# docs.n8n.io/deploy/host-n8n/install-options/install-with-docker
docker volume create n8n_data
docker run -d --name n8n -p 5678:5678 \
-v n8n_data:/home/node/.n8n \
n8nio/n8n
# On a one-click platform you do exactly this through the UI: attach a
# persistent volume and give it /home/node/.n8n as the mount path. The
# platform's own docs are blunt about the default — runtime storage is
# ephemeral, and anything that must survive a redeploy needs a volume.
#
# Flowise is the same shape at a different path. Its published compose file
# mounts ~/.flowise onto /home/node/.flowise, again because the container
# runs as the non-root "node" user:
# github.com/FlowiseAI/Flowise/blob/main/docker/docker-compose.ymlContoh Docker Compose milik n8n sendiri melakukan kelalaian yang sama secara sengaja dan mengatakannya terang-terangan: tanpa database service, n8n jatuh ke SQLite bawaannya, yang tersimpan di dalam container kecuali Anda memasang volume untuknya. Memasang volume hanya satu kolom di panel deploy. Salah mount path adalah versi mahal dari kolom itu, karena volume yang dipasang di lokasi yang tidak pernah ditulis prosesnya terlihat persis seperti volume yang bekerja.
Platform yang saya maksud adalah Helipod, yang memasang harga dalam rupiah, menerima QRIS dan transfer bank tanpa kartu kredit, serta memuat n8n dan Flowise di antara template sekali kliknya bersama Supabase, PostgreSQL, Redis dan Uptime Kuma. Semua yang dibahas di bawah berlaku untuk platform container mana pun, bukan hanya yang satu ini: helipod.io
SQLite adalah database default, dan n8n memakainya untuk menyimpan credential, riwayat execution, dan workflow. Direktori data-nya menyimpan lebih dari satu file itu: encryption key, log instance, dan aset yang dibutuhkan fitur source control. Itu seluruh instance dalam satu folder, yang praktis begitu Anda memasangnya dan total begitu Anda tidak.
| Isi direktori data n8n | Harga restart tanpa volume |
|---|---|
| Database SQLite, definisi workflow | Semua workflow yang Anda bangun, kembali ke canvas kosong |
| Database SQLite, credential tersimpan | Setiap API key dan OAuth token yang disimpan, terenkripsi dan hilang |
| Database SQLite, riwayat execution | Log jalan yang biasa Anda pakai untuk men-debug workflow gagal |
| File settings, encryption key hasil generate | Kuncinya sendiri, sehingga database yang direstore pun tetap tak terbaca |
| Log instance dan aset source control | Diagnostik lokal dan kunci yang dipakai integrasi Git untuk menandatangani |
Restart adalah bagian yang menjebak, karena tidak selalu Anda yang memicunya. Docker image n8n merestart prosesnya otomatis setelah heap habis, jadi container tanpa volume bisa mengosongkan dirinya sendiri saat tidak ada yang sedang men-deploy apa pun. Perlakukan persistent volume sebagai kolom pertama yang Anda isi, bukan langkah hardening yang bisa menyusul.
n8n menyusun webhook URL-nya dengan menggabungkan N8N_PROTOCOL, N8N_HOST dan N8N_PORT, dan dokumentasinya menjelaskan dengan jelas kenapa itu rusak di belakang reverse proxy: n8n berjalan di port 5678 secara internal, sementara proxy memaparkannya ke internet di port 443. Setiap managed platform adalah reverse proxy. Biarkan default-nya dan editor akan memberi Anda alamat callback di localhost, yang bisa Anda tempel dengan santai ke payment gateway atau GitHub App, lalu Anda habiskan satu sore bertanya-tanya kenapa tidak ada yang masuk.
# n8n composes its webhook URL from N8N_PROTOCOL + N8N_HOST + N8N_PORT.
# The defaults are http, localhost and 5678 — so those are the values it
# hands to an external service if you never override them.
# Wrong: the editor shows http://localhost:5678/webhook/abc and no payment
# gateway, GitHub App or WhatsApp Business callback can ever reach it.
N8N_HOST=localhost
N8N_PROTOCOL=http
N8N_PORT=5678
# Right: the container still listens on 5678, but n8n now advertises the
# address the outside world actually uses.
N8N_HOST=n8n.example.com
N8N_PROTOCOL=https
N8N_WEBHOOK_URL=https://n8n.example.com/
N8N_EDITOR_BASE_URL=https://n8n.example.com/
N8N_PROXY_HOPS=1
# WEBHOOK_URL is the name most tutorials still use. It is deprecated from
# n8n 2.35.0 as an alias of N8N_WEBHOOK_URL and logs a warning at startup,
# so configure a new instance with the new name.
#
# Flowise's equivalent is one variable, used for the links inside password
# reset and workspace invite emails:
APP_URL=https://flowise.example.comPerbaikannya adalah menyebut base URL publik secara eksplisit dan memberi tahu n8n berapa proxy yang ada di depannya. Perhatikan nama variabelnya sambil mengerjakan itu: N8N_WEBHOOK_URL menggantikan WEBHOOK_URL, yang deprecated sejak n8n 2.35.0 dan mencatat peringatan saat startup. Sebagian besar panduan masih memakai nama lama, jadi instance yang dikonfigurasi dari tutorial memulai hidupnya dengan deprecation warning di log. Flowise punya versi lebih kecil dari kebutuhan yang sama pada APP_URL, yang menjadi tautan di email reset password dan undangan workspace-nya.
Ini kegagalan yang paling mahal dan paling terlambat muncul. Jika N8N_ENCRYPTION_KEY tidak diset, n8n membuat kunci acak saat launch pertama, menyimpannya ke folder data, dan memakainya untuk mengenkripsi setiap credential sebelum masuk database. Tidak ada satu pun bagian antarmuka yang memperlihatkan nilai itu. Redeploy tanpa foldernya, atau restore database di sebelah kunci baru yang baru saja digenerate, dan semua credential berubah menjadi ciphertext yang Anda miliki tetapi tidak bisa Anda baca.
# Generate both keys BEFORE the first boot and paste them into the platform's
# secret variables. Setting them afterwards does not recover anything.
openssl rand -hex 32 # for n8n
openssl rand -hex 32 # for Flowise
# n8n: left unset, n8n generates a random key on first launch and writes it
# into the settings file inside the data folder. In queue mode the same value
# must be set on every worker.
N8N_ENCRYPTION_KEY=paste_the_first_hex_string_here
# Flowise: same failure, different name. The docs name the symptom exactly —
# "Credentials could not be decrypted" — and its cause: the encryption key
# was regenerated, or the path it was stored at changed.
FLOWISE_SECRETKEY_OVERWRITE=paste_the_second_hex_string_here
# Flowise puts no format restriction on that value. Hex from openssl is
# simply easier to copy between a password manager and a deploy panel than a
# passphrase somebody types twice and mistypes once.Flowise gagal dengan cara yang sama dan bahkan menerbitkan pesan errornya, yang justru membantu. Dokumentasinya mencatat bahwa encryption key kadang digenerate ulang atau path penyimpanannya berubah, bahwa hasilnya adalah error credentials could not be decrypted, dan bahwa menyetel FLOWISE_SECRETKEY_OVERWRITE membuat kunci yang sama selalu dipakai. Dua variabel, digenerate sekali, disimpan di password manager, ditempel ke secret variable milik platform. Itu seluruh mitigasinya.
Setel kedua kunci sebelum boot pertama. Kalimat n8n cukup teliti: Anda bisa memberikan kunci sendiri selama kunci itu belum masuk ke file settings. Setelah n8n membuat kuncinya sendiri, mengubah variabel itu tidak mengenkripsi ulang apa pun, melainkan membuat credential yang tersimpan tidak terbaca. Karena itu rotasi kunci adalah fitur opt-in terpisah yang dokumentasinya dibuka dengan perintah mengambil full backup database lebih dulu.

Default Flowise adalah jebakan yang tidak selesai dengan satu volume mount. Database SQLite-nya mendarat di folder .flowise di bawah home directory user container, dan file yang diupload di subfolder storage di dalamnya, itu masih rapi. Encryption key dan log-nya default ke tempat yang sama sekali lain, di bawah package server di dalam direktori aplikasi. Pasang volume hanya di path database dan Anda menyimpan semua chatflow sambil kehilangan kunci yang mendekripsi credential yang menempel padanya.
# Flowise writes four separate things to four separate defaults, and those
# defaults do not all sit under one folder.
#
# DATABASE_PATH your-home-dir/.flowise
# BLOB_STORAGE_PATH your-home-dir/.flowise/storage
# SECRETKEY_PATH Flowise/packages/server (somewhere else)
# LOG_PATH Flowise/packages/server/logs (somewhere else)
#
# Mount a volume at the database path only and you keep every chatflow while
# losing the key that decrypts the credentials attached to them. Point all
# four at the mount, then mount once.
DATABASE_TYPE=sqlite
DATABASE_PATH=/home/node/.flowise
BLOB_STORAGE_PATH=/home/node/.flowise/storage
SECRETKEY_STORAGE_TYPE=local
SECRETKEY_PATH=/home/node/.flowise
LOG_PATH=/home/node/.flowise/logsDetail user itu lebih penting daripada kelihatannya, sebab mount di path yang tidak pernah disentuh prosesnya sama sekali tidak memunculkan error. Flowise tetap start, tetap bekerja, dan menulis state sebenarnya ke lapisan ephemeral di sebelah volume kosong Anda. Satu-satunya cara menangkapnya adalah membuat satu credential, redeploy dengan sengaja, lalu memastikan credential itu masih bisa didekripsi, tes dua menit yang layak dikerjakan saat Anda belum punya apa pun untuk hilang.
Sebuah instance Flowise adalah gudang API key milik orang lain, termasuk key model provider dan key vector database Anda, yang duduk di belakang satu web UI. Sejak v3.0.1 ia melakukan authentication dengan benar, memakai sistem berbasis Passport.js dan JWT yang disimpan di HTTP-only cookie, dan pasangan lama FLOWISE_USERNAME serta FLOWISE_PASSWORD adalah jalur deprecated yang dipertahankan agar install lama bisa login sekali untuk mengklaim akun admin baru. Sebagian besar tutorial belum menyusul, dan dari situ instance berakhir di jalur deprecated, atau tanpa jalur sama sekali.
# Flowise, from v3.0.1, authenticates with Passport.js and JWTs held in
# HTTP-only cookies. Its docs warn that leaving these unset falls back to
# default values, which raises the chance of an attacker forging a valid
# token and impersonating a user. EXPRESS_SESSION_SECRET defaults to the
# literal string "flowise".
# docs.flowiseai.com/configuration/authorization/app-level
JWT_AUTH_TOKEN_SECRET=first_openssl_rand_hex_32_output
JWT_REFRESH_TOKEN_SECRET=second_openssl_rand_hex_32_output
EXPRESS_SESSION_SECRET=third_openssl_rand_hex_32_output
TOKEN_HASH_SECRET=fourth_openssl_rand_hex_32_output
SECURE_COOKIES=true
NUMBER_OF_PROXIES=1 # or the rate limiter sees only the proxy's IP
# FLOWISE_USERNAME and FLOWISE_PASSWORD still appear in most tutorials.
# That is the deprecated path, kept so an existing install can authenticate
# once while claiming the new admin account.
# n8n's side of the same problem, from its user-management page:
# N8N_USER_MANAGEMENT_JWT_SECRET generated on start if left unset
# N8N_MFA_ENABLED true by default
# N8N_SECURE_COOKIE true, so cookies need HTTPSPoin yang lebih tajam adalah apa yang terjadi kalau Anda tidak menyetel apa pun. Dokumentasi Flowise menganjurkan Anda mengonfigurasi sendiri variabel JWT dan secret token, karena kalau tidak nilai default yang dipakai, dan itu memperbesar peluang penyerang memalsukan token yang valid lalu menyamar sebagai pengguna. Default terdokumentasi untuk EXPRESS_SESSION_SECRET adalah string flowise apa adanya. Empat nilai acak plus NUMBER_OF_PROXIES, diset saat deploy, sudah menutupnya. Padanannya di n8n adalah N8N_USER_MANAGEMENT_JWT_SECRET, yang juga digenerate saat start kalau dibiarkan, satu alasan lagi kenapa folder datanya bukan opsional.

Kedua project tidak menerbitkan angka memori minimum dan saya tidak akan mengarangnya. Yang memang dikatakan dokumentasi n8n adalah bahwa ia tidak membatasi berapa banyak data yang boleh diambil dan diproses sebuah node, dan justru sifat itulah yang mengubah workflow yang tampak wajar menjadi heap habis di container kecil. Dokumen itu juga mendaftar apa yang menggelembungkan satu run: ukuran payload JSON, ukuran binary data, jumlah node, node Code secara khusus, dan manual execution, karena run manual membuat salinan kedua datanya untuk frontend.
# The line to grep for in the logs of a small plan:
# Allocation failed - JavaScript heap out of memory
#
# n8n's Docker image restarts the process automatically after that, so the
# symptom a user reports is a workflow that "sometimes fails" rather than an
# instance that is visibly down. Watch restart counts, not just uptime.
# Give V8 a ceiling under the container's limit before buying more RAM.
# The number below is an example — keep it under whatever the plan grants.
NODE_OPTIONS=--max-old-space-size=768
# Stop execution history growing until the volume becomes the problem.
# These are n8n's own defaults, worth knowing before changing them.
EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=336 # hours, so fourteen days
EXECUTIONS_DATA_PRUNE_MAX_COUNT=10000 # 0 means no limitFlowise menerbitkan rekomendasi production, bukan angka minimum, dan itu lebih cocok dibaca sebagai kalibrasi ketimbang sebagai syarat: queue mode dengan dua main server yang di-load-balance mulai dari empat vCPU dan delapan gigabyte RAM masing-masing, ditambah empat worker berukuran sama. Itu menggambarkan deployment ramai yang dipakai bersama, bukan chatflow satu builder, tetapi tetap memberi tahu Anda dari arah mana tekanannya datang. Di plan satu atau dua gigabyte, jalankan kedua service secara terpisah, pecah job besar menjadi run yang lebih kecil, dan siapkan diri bahwa node Code adalah tempat plafonnya muncul lebih dulu.
Sebelum mempercayai instance baru, rusakkan dulu dengan sengaja. Simpan satu credential, jalankan satu workflow, lalu redeploy memakai tombol platform itu sendiri dan periksa apakah credential-nya masih bisa didekripsi. Satu tes itu menangkap volume yang belum dipasang, mount path yang salah, dan encryption key yang belum diset sekaligus, pada satu-satunya saat ketika kehilangan ketiganya tidak merugikan Anda.
Template sekali klik layak dipakai, hanya saja namanya salah menjual. Yang benar-benar dikerjakan tombol itu adalah build, start, routing, dan enkripsi jalur transportnya, bagian yang dulu memakan satu sore untuk konfigurasi nginx. Yang tidak bisa ia ketahui adalah bahwa container ini menyimpan sebuah database, sebuah secret, dan sebuah identitas publik. Jadi empat keputusan tetap milik Anda pada setiap template yang stateful: pasang volume, publikasikan URL yang sebenarnya, patok encryption key, dan setel secret authentication sebelum request pertama datang.
Sumber