SSD Nodes Learn Hosting plans →
Panduan Matt ConnorOleh Matt Connor · Diperbarui 2026-08-29

Cara Self-Host OpenAnalytics di VPS

Ketahui kebutuhan nyata sebelum memasang OpenAnalytics: ClickHouse, Postgres, Valkey, RAM 4 GB, ruang kosong 25 GB, dan empat record DNS, lalu ikuti instalasinya.

Jejak sumber daya sebelum langkah pertama

Untuk melakukan self-hosting OpenAnalytics, Anda memerlukan Linux VPS dengan RAM sekitar 4 GB, ruang disk kosong 25 GB, Docker dengan plugin Compose, dan empat record DNS yang sudah mengarah ke server tersebut. Itulah persyaratan utamanya, dan informasi ini seharusnya disampaikan sebelum perintah pertama, bukan sesudahnya.

Stack ini terdiri atas enam service aplikasi dan tiga penyimpanan data. Postgres menyimpan control plane: akun, situs, API key, dan tautan berbagi. ClickHouse menyimpan event mentah serta hasil agregasi yang dibaca dashboard. Valkey berjalan dua kali: satu sebagai antrean event persisten dan satu sebagai cache yang boleh hilang, karena kedua fungsi tersebut memerlukan kebijakan eviction yang berlawanan. Hanya satu proses, yaitu query gateway, yang diizinkan membaca ClickHouse. Proses ini memverifikasi signature Ed25519 pada setiap query envelope sebelum menjalankannya.

Jika yang Anda inginkan adalah satu binary dan satu file konfigurasi, stack ini bukan pilihan yang tepat. GoatCounter adalah opsi single-binary dalam kategori ini: satu executable Go, SQLite secara default, dan tanpa database eksternal. Stack yang lebih berat ini menyediakan funnel, web vitals, atribusi pendapatan dari akun Stripe milik Anda sendiri, serta server MCP (model context protocol). Memilih antara tool analytics self-hosted adalah artikel yang membahas trade-off tersebut. Panduan ini mengasumsikan Anda sudah mengambil keputusan.

Arahkan record DNS ke server terlebih dahulu

Keempat subdomain harus mengarah ke IP publik server sebelum Anda memulai apa pun. Caddy meminta sertifikat Let's Encrypt saat pertama kali dijalankan, dan challenge akan gagal jika nama tersebut belum dapat di-resolve.

  • app.example.com menyajikan dashboard.
  • api.example.com menyajikan API dan callback OAuth.
  • c.example.com menyajikan collector dan skrip tracker.
  • rt.example.com menyajikan stream realtime.

Gunakan empat record A, atau satu record A dan tiga record CNAME yang mengarah ke record tersebut. Konfirmasikan dengan dig +short app.example.com sebelum melanjutkan. Nama yang baru Anda tambahkan satu menit lalu masih dapat di-cache sebagai NXDOMAIN oleh resolver mana pun yang digunakan Let's Encrypt. Karena itu, jika percobaan penerbitan sertifikat pertama gagal, tunggu beberapa saat dan periksa log Caddy. Menjalankan ulang instalasi tidak mempercepat propagasi DNS.

Cara melakukan self-hosting OpenAnalytics dengan Docker Compose

Checkout tag rilis. Branch default digunakan untuk pengembangan, sedangkan tag rilis sesuai dengan image yang dipublikasikan. Perintah di bawah mengasumsikan Docker dan plugin Compose sudah terpasang. Panduan menjalankan service Docker Compose pada VPS membahas hal tersebut.

git clone https://github.com/OpenLabs-so/openanalytics
cd openanalytics
git checkout "$(git tag -l 'v*' --sort=-v:refname | sed '/-/d' | head -1)"
cd infra/selfhost
./generate-secrets.sh --domain example.com --email you@example.com --with-geoip
docker compose pull && docker compose up -d

sed '/-/d' pada baris checkout mengabaikan tag prarilis. Dengan demikian, Anda menggunakan versi stabil terbaru, bukan release candidate. --with-geoip mengambil database kota DB-IP selama proses pembuatan. Jika langkah ini dilewati, setiap event akan memiliki nilai null untuk negara. Akibatnya, tampilan geografis tidak menampilkan apa pun. Anda dapat menambahkannya nanti dengan menjalankan infra/selfhost/geoip/fetch-dbip.sh, menetapkan GEOIP_DB_PATH=/geoip/dbip-city-lite.mmdb dalam env/collector.env, lalu membuat ulang collector dengan docker compose up -d --force-recreate collector. Database tersebut diperbarui setiap bulan. Ulangi proses pengambilan setiap bulan agar data kota tetap akurat.

Cadangkan secret yang dihasilkan sebelum melanjutkan

Generator menulis tiga hal. .env berisi nama domain dan referensi image. env/*.env berisi satu file secret untuk setiap service. docker-compose.override.yml berisi tiga pasangan kunci Ed25519 sebagai scalar blok YAML, karena PEM multi-baris tidak dapat disimpan dalam file env. Semua file tersebut dikecualikan dari Git melalui git-ignore, dan tidak ada yang dapat dibuat ulang dengan nilai yang sama.

Salin file tersebut ke luar mesin sekarang. Setiap kehilangan memiliki dampak tertentu:

  • Jika password store hilang, Anda tidak dapat mengakses Postgres dan ClickHouse. Password tersebut hanya dapat direset dari dalam container.
  • Jika OA_CREDENTIAL_KEYRING hilang, semua kredensial pihak ketiga yang tersimpan tidak dapat dipulihkan. Akibatnya, siapa pun yang pernah menghubungkan akun Stripe harus menghubungkannya lagi.
  • Jika ANONYMOUS_IDENTITY_SECRET hilang, identitas pengunjung dibuat ulang dari awal. Semua pengunjung kemarin dihitung sebagai pengunjung baru, dan perubahan tersebut terlihat pada chart.
  • Jika AUTH_SECRET hilang, semua session menjadi tidak valid sehingga semua pengguna harus login kembali.
  • Jika private key untuk signing hilang, lakukan rotasi pasangan kunci. Tidak ada data yang hilang.

Dua secret harus identik byte demi byte pada masing-masing dari dua file. ANONYMOUS_IDENTITY_SECRET muncul dalam collector.env dan worker.env karena collector menghitung hash pengunjung dan worker menuliskannya. OA_CREDENTIAL_KEYRING muncul dalam api.env dan worker.env. Semua secret lainnya sengaja dibatasi tepat untuk satu service, dan service yang menerima secret yang seharusnya tidak dimilikinya akan berhenti, bukan start.

Jalankan stack dan periksa kondisinya

grep OA_IMAGE .env
docker compose pull
docker compose up -d
docker compose logs -f migrate
docker compose ps

migrate menerapkan skema Postgres dan ClickHouse, lalu berhenti. Karena itu, container migrate yang berhenti adalah kondisi akhir yang benar. tracker-build mengompilasi oa.js ke dalam volume yang disajikan oleh Caddy, lalu berhenti juga. Semua komponen lainnya harus menampilkan status healthy di docker compose ps. Service yang terus-menerus restart hampir selalu gagal dalam validasi environment. Log mencetak semua masalah dalam satu daftar, bukan satu masalah pada setiap restart. Dua penyebab yang umum adalah variabel yang dibiarkan kosong, yang ditolak dan tidak dianggap belum disetel, serta secret yang ditempatkan dalam file service yang salah.

Pada arm64, atau jika menggunakan branch, image yang dipublikasikan tidak tersedia sehingga Anda harus melakukan build secara lokal dengan docker compose up -d --build. Host dengan RAM 4 GB akan kehabisan memori di tengah proses build tersebut. Tambahkan swap terlebih dahulu. Swap hanya diperlukan selama proses build:

fallocate -l 4G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab

Proses build memerlukan waktu sekitar sepuluh menit. Proses pull hanya memerlukan beberapa menit. Karena itu, image release disediakan.

Klaim akun pertama segera

Buka https://app.example.com. Deployment yang belum pernah digunakan untuk login tidak menampilkan formulir login; deployment tersebut menawarkan pembuatan akun pertama. Akun itu menjadi akun dengan hak istimewa secara permanen, dan hanya akun tersebut yang dapat melihat layar pengaturan deployment. Setelah akun tersebut dibuat, route mengembalikan 409 sehingga tidak ada orang yang dapat masuk setelah Anda. Lakukan segera setelah stack berjalan normal, bukan pada minggu berikutnya.

Instal tracker

Tambahkan situs di dashboard, lalu dashboard akan memberikan tag. Bentuknya tetap:

<script
  async
  src="https://c.example.com/oa.js"
  data-key="YOUR_TRACKING_KEY"
  data-collector="https://c.example.com"
></script>

Tempatkan tag tersebut di head halaman. Kunci pelacakan memang bersifat publik, jadi kunci itu diletakkan di HTML yang dapat dibaca siapa pun. Skrip ini memasang window.oa, dan pemanggilan seperti oa("track", ...) diantrekan oleh stub lalu dikirim setelah file selesai dimuat. Dengan demikian, event kustom yang dipicu lebih awal tidak hilang. Jika bagian lain pada halaman sudah menggunakan window.oa, tracker akan memasang window.openanalytics sebagai gantinya. Jika situs yang sama juga tersedia sebagai layanan onion, jangan sertakan tag ini dalam build tersebut. Skrip yang diambil dari c.example.com akan mengarahkan pengunjung Tor Browser kembali ke clearnet dan menghubungkan kedua alamat itu dalam pemuatan halaman yang sama.

Selanjutnya, periksa seluruh alur dari ujung ke ujung:

curl -s https://c.example.com/oa.js -o /dev/null -w '%{http_code} %{size_download}\n'
curl -s https://api.example.com/health | head -c 200
docker compose logs --tail=50 worker | grep -i batch

Perintah pertama seharusnya mencetak 200 dan beberapa kilobyte. Muat halaman di situs Anda, lalu cari baris batch di log worker dalam hitungan detik. Collector mengembalikan 202 segera setelah menerima event, sedangkan 202 berarti diantrekan, bukan disimpan. Worker memindahkan event ke ClickHouse. Jika event diterima tetapi tidak muncul di dashboard, berarti worker terblokir. Kedalaman queue Valkey yang terus meningkat mengonfirmasi hal ini. Penyebab yang umum adalah kredensial ClickHouse yang salah di worker.env atau grant yang tidak ada pada tabel yang baru ditambahkan oleh migration.

Pertahankan collector tetap publik dan dashboard di balik autentikasi

Caddy disertakan dalam file compose dan memperoleh sertifikat untuk keempat nama tersebut secara mandiri, sehingga jalur default tidak memerlukan konfigurasi proxy tambahan. Jika server sudah menjalankan reverse proxy nginx, tempatkan stack di belakang infra/selfhost/nginx.conf.example yang disediakan, dan pertahankan penanganan header yang ada:

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header CF-Connecting-IP "";
proxy_set_header True-Client-IP "";
proxy_set_header Fly-Client-IP "";

Collector menghitung hash pengunjung harian dari IP klien, sehingga alamat tersebut harus diambil dari koneksi dan tidak pernah dari header. Meneruskan CF-Connecting-IP dari hop yang tidak tepercaya memungkinkan siapa pun mengklaim alamat apa pun. Hal ini sekaligus merusak geolokasi dan menggelembungkan jumlah pengunjung.

Akses terbagi berdasarkan hostname. c. dan rt. harus dapat dijangkau oleh setiap pengunjung dari setiap situs yang Anda ukur, sehingga jangan menempatkan basic auth atau daftar izin IP di depan kedua hostname tersebut. app. dan api. hanya perlu dapat dijangkau oleh pengguna yang sudah login. Auth bawaan aplikasi melindungi dashboard: login dengan kata sandi aktif secara default melalui AUTH_PASSWORD_SIGNIN=enabled di env/api.env, sedangkan tombol Google atau GitHub hanya muncul jika client ID dan client secret untuk provider tersebut sama-sama tersedia. Magic link memerlukan mail transport. Tanpanya, API hanya menulis permintaan pengiriman ke outbox, sehingga tidak ada pesan yang terkirim dan tidak ada error yang muncul. Jika aplikasi self-hosted lain sudah berada di balik satu login Authentik, tentukan sejak awal apakah dashboard ini akan bergabung dengan aplikasi tersebut atau tetap menggunakan akun sendiri, karena akun pertama yang Anda buat di sini akan menjadi akun dengan hak istimewa secara permanen.

Satu pengaturan menentukan apakah dashboard dapat berfungsi. AUTH_TRUSTED_ORIGINS di env/api.env harus sama persis dengan origin dashboard. Jika nilainya salah atau tidak ada, API tidak mengirim header CORS (cross-origin resource sharing), browser menolak setiap panggilan, dan dashboard hanya menampilkan tata letaknya tanpa data, sementara docker compose ps melaporkan bahwa semuanya sehat.

Saat mengedit konfigurasi proxy, tangani juga trafik otomatis. Crawler mengakses collector seperti pengunjung lain, dan tampilan halaman mereka masuk ke ClickHouse serta ke angka statistik Anda. Memblokir crawler AI di server mencegah sebagian trafik tersebut masuk ke database sebelum mengurangi akurasi dan menghabiskan ruang disk.

Makna cookieless dalam konteks ini dan biayanya bagi Anda

Tidak ada cookie. Identitas pengunjung berupa hash dengan salt, salt tersebut dirotasi setiap hari, dan alamat IP mentah tidak pernah disimpan. Geolokasi ditentukan secara lokal menggunakan file DB-IP pada disk Anda sendiri, sehingga tidak ada permintaan pencarian tentang pengunjung yang meninggalkan host. Menjalankan pencarian secara lokal menghilangkan vendor, bukan datanya. Batasan yang sama terlihat ketika Anda menjalankan instance SearXNG sendiri dan IP server Anda menjadi informasi yang dilihat mesin pencari.

Manfaatnya adalah tidak adanya pengenal yang disimpan pada perangkat pengunjung. Hal inilah yang secara khusus dapat menyebabkan tracker termasuk dalam aturan persetujuan ePrivacy di UE. Karena itu, konfigurasi yang hanya menghasilkan data agregat seperti ini umumnya dijalankan tanpa banner persetujuan. GDPR tetap berlaku terhadap data yang Anda simpan dan lamanya penyimpanan. Penentuan untuk kasus Anda dilakukan oleh penasihat hukum Anda, bukan oleh README.

Konsekuensinya adalah identitas tidak dapat dilacak lintas hari. Karena salt dirotasi, seseorang yang berkunjung pada hari Senin lalu kembali pada hari Rabu dihitung sebagai dua pengunjung. Ini memang dirancang demikian dan tidak ada solusinya. Jumlah pengunjung unik harian akurat. Jumlah pengunjung unik mingguan dan bulanan dihitung dari data harian dan akan melebih-lebihkan jangkauan. Karena itu, angka "pengunjung yang kembali" untuk rentang waktu panjang tidak mengukur hal yang dinyatakan oleh labelnya. Sesi dan perjalanan pengunjung dapat diandalkan dalam satu hari yang sama. Rotasi ANONYMOUS_IDENTITY_SECRET memiliki efek yang sama seperti batas pergantian hari. Perlakukan rotasi tersebut sebagai perubahan data, bukan pemeliharaan rutin.

Collector mematuhi Do Not Track dan Global Privacy Control, yaitu sinyal browser yang memberi tahu situs agar tidak menjual atau membagikan data pribadi. Tag script memiliki switch tersendiri untuk tujuan yang sama: data-respect-gpc, data-respect-dnt, dan data-require-consent. Switch data-require-consent menahan seluruh pengumpulan data sampai persetujuan diberikan dan menyimpan jawaban tersebut dalam localStorage dengan key oa.consent. Menetapkan data-storage="none" akan menonaktifkan penyimpanan browser sepenuhnya.

Mengapa disk penuh setelah enam bulan

Inilah yang biasanya membuat server analytics self-hosted gagal beroperasi, dan penyebabnya biasanya bukan event.

Mulai dari image. Satu release menerbitkan sepuluh image dengan total sekitar 13 GB di disk. Saat upgrade, generasi baru di-pull sebelum generasi lama dihapus, sehingga selama beberapa waktu terdapat dua generasi. Ini sudah mencakup sebagian besar kebutuhan 25 GB, bahkan sebelum satu page view pun diterima.

Berikutnya, snapshot. snapshot.sh menghentikan stack, mengarsipkan kedua volume data beserta semua secret, lalu menjalankannya kembali. Salinan cold adalah satu-satunya jenis yang aman dalam kondisi ini karena ClickHouse menggabungkan part di background, dan salinan yang dibuat saat proses merge berlangsung tidak konsisten. upgrade.sh membuat satu snapshot secara otomatis sebelum setiap upgrade, sehingga arsip menumpuk di disk yang sama sampai Anda membatasi jumlahnya.

./snapshot.sh create --label before-something-risky
./snapshot.sh list
./snapshot.sh --keep 3

Pada host yang hampir mencapai batas kapasitas, hapus generasi sebelumnya sebelum melakukan upgrade. Tindakan ini aman saat stack sedang berjalan karena image yang digunakan container yang sedang berjalan masih direferensikan:

docker image prune -a -f

Berikutnya, periksa event itu sendiri. ClickHouse mengompresi data kolumnar secara agresif, sehingga volume data mentah bertambah lebih lambat daripada perkiraan kebanyakan orang, dan tabel rollup yang dibaca dashboard berukuran kecil dibandingkan tabel mentah. Lakukan pengukuran, bukan perkiraan:

docker system df -v
docker compose exec clickhouse df -h /var/lib/clickhouse

Untuk melihat ukuran per tabel, jalankan ini menggunakan kredensial ClickHouse yang ditulis oleh generator di bawah infra/selfhost/env/:

SELECT table, formatReadableSize(sum(bytes_on_disk)) AS size, sum(rows) AS row_count
FROM system.parts
WHERE active
GROUP BY table
ORDER BY sum(bytes_on_disk) DESC;

Catat hasil tersebut pada minggu pertama, lalu ulangi pada minggu keempat. Dua titik data cukup untuk menghitung laju pertumbuhan, dan laju pertumbuhan menunjukkan kapan volume perlu diperbesar. Panduan self-hosting belum mendokumentasikan opsi retention atau time-to-live untuk event mentah per August 2026. Karena itu, tentukan ukuran disk berdasarkan laju yang Anda ukur, bukan dengan mengasumsikan bahwa row lama akan kedaluwarsa sendiri.

Ada satu jebakan penghapusan yang perlu diketahui sebelum menimbulkan masalah. Penghapusan site atau account memasukkan pekerjaan ke antrean worker, dan worker tersebut memerlukan CLICKHOUSE_MAINTENANCE_USER serta CLICKHOUSE_MAINTENANCE_PASSWORD, dengan user oa_maintenance yang sesuai dan sudah ada di ClickHouse. Tanpa pengaturan tersebut, pekerjaan penghapusan akan tetap berada dalam antrean selamanya. Site menghilang dari dashboard, tetapi setiap row tetap berada di disk. Akibatnya, pembersihan hanya terlihat berhasil, tanpa mengembalikan ruang disk.

Pembaruan, dan tiga biaya

git fetch --tags
git checkout "$(git tag -l 'v*' --sort=-v:refname | sed '/-/d' | head -1)"
cd infra/selfhost
./upgrade.sh

upgrade.sh mencetak tiga biaya sebelum menjalankan tindakan. Downtime adalah risiko nyata: peristiwa yang dicoba saat collector tidak aktif akan hilang karena tracker tidak mengulangi pengirimannya. Rollback menyebabkan kehilangan data karena rollback.sh --to backups/<snapshot> mengganti kedua penyimpanan secara keseluruhan dan membuang setiap baris yang ditulis setelah snapshot tersebut dibuat. Disk adalah biaya ketiga, yaitu tumpukan snapshot yang dijelaskan di atas.

Dua aturan restart mudah diterapkan secara keliru. Aktifkan query gateway sebelum API karena API yang lebih baru mengirimkan field query yang ditolak oleh gateway yang lebih lama. ClickHouse harus dibuat ulang, bukan sekadar di-restart, karena docker compose restart menggunakan kembali environment asli container dan mengabaikan perubahan Anda tanpa pesan:

docker compose up -d --force-recreate clickhouse

Dashboard memiliki jebakan yang sama. Tiga origin NEXT_PUBLIC_* di dalam env/web.env dikompilasi ke dalam bundle browser dan disubstitusikan saat container dimulai. Karena itu, dashboard yang memanggil hostname yang salah diperbaiki dengan docker compose up -d --force-recreate web, bukan dengan restart. Log web container mencetak origin yang digunakan saat container dimulai. Ini adalah cara tercepat untuk memastikan perbaikan telah diterapkan.

Jika ClickHouse menolak untuk start setelah konfigurasi diedit, baca baris pertama log-nya. Baris yang diawali oa-entrypoint: menunjukkan bahwa entrypoint menolak nilai yang Anda tetapkan. Selain itu, biasanya berarti file konfigurasi berisi XML yang tidak valid. Penyebab yang paling umum adalah tanda hubung ganda di dalam komentar XML, yang tidak diizinkan.

AGPL-3.0, dan nama

Kode ini dilisensikan berdasarkan AGPL-3.0. Menjalankannya tanpa modifikasi untuk situs Anda sendiri sama sekali tidak menimbulkan kewajiban untuk memublikasikan kode. Kewajiban tersebut dimulai ketika Anda memodifikasi kode lalu menjalankan versi yang telah dimodifikasi itu sebagai layanan jaringan: lisensi kemudian mewajibkan Anda menawarkan source code yang telah dimodifikasi kepada pengguna layanan tersebut. Hal ini mencakup penyediaan dashboard kepada klien pada instance Anda, serta membundel kode tersebut ke dalam produk yang Anda jual. Menyimpan perubahan Anda dalam fork publik sudah memenuhi kewajiban tersebut tanpa proses tambahan.

Merek terpisah dari kode. Nama "OpenAnalytics" dan domain hosted milik proyek mengidentifikasi instance yang dioperasikan oleh pembuatnya, dan keduanya bukan bagian dari pemberian lisensi. Deployment Anda menjalankan software tersebut tanpa menggunakan mereknya, jadi berikan nama tersendiri untuk layanan itu sebelum menawarkannya kepada pelanggan yang membayar.

FAQ

Dapatkah saya menjalankan OpenAnalytics pada VPS 1 GB?

Tidak. Proyek ini memerlukan sekitar 4 GB RAM dan 25 GB ruang disk kosong karena satu deployment menjalankan enam service aplikasi bersama Postgres, ClickHouse, dan dua instance Valkey. ClickHouse sendiri bukan proses kecil. Pada server 1 GB, container akan start, lalu kernel out-of-memory killer menghentikan salah satunya, biasanya ClickHouse. Jika paket 1 GB merupakan batas yang tidak dapat diubah, gunakan tool single-binary seperti GoatCounter, yang berjalan dengan SQLite tanpa database eksternal.

Hal itu perlu ditanyakan kepada pengacara Anda, tetapi fakta teknisnya mendukung Anda. Tidak ada cookie, identitas pengunjung berupa hash dengan salt yang berganti setiap hari, dan alamat IP mentah tidak pernah disimpan. Jadi, tidak ada data permanen yang ditulis untuk mengidentifikasi pengunjung. GDPR tetap mengatur data yang Anda simpan dan lamanya data tersebut disimpan. Jika Anda ingin pengumpulan data secara eksplisit bergantung pada persetujuan, tetapkan data-require-consent pada tag script: tracker kemudian tidak mengumpulkan apa pun sampai persetujuan diberikan, dan menyimpan jawabannya di localStorage dengan nilai oa.consent.

Mengapa event mengembalikan 202 tetapi tidak pernah muncul di dashboard?

202 berarti collector menerima dan memasukkan event ke queue, bukan menyimpannya. Worker mengosongkan queue tersebut ke ClickHouse. Jadi, dashboard yang kosong dengan request yang berhasil menunjukkan masalah pada worker. Baca docker compose logs --tail=50 worker dan pantau kedalaman queue Valkey. Queue yang terus bertambah berarti worker terblokir. Penyebab yang umum adalah kredensial ClickHouse yang salah di worker.env atau grant yang tidak ada pada tabel yang dibuat oleh migration terbaru.

Mengapa dashboard kosong padahal semua container sehat?

Periksa AUTH_TRUSTED_ORIGINS di env/api.env terlebih dahulu. Nilainya harus sama persis dengan origin dashboard. Jika tidak sama, API tidak mengirim header CORS. Akibatnya, browser menolak setiap call, sehingga Anda melihat layout yang berfungsi tetapi tanpa data. Hal kedua yang perlu diperiksa adalah tiga nilai NEXT_PUBLIC_* di env/web.env. Nilai tersebut disubstitusi saat web container start. Untuk memperbaikinya, diperlukan docker compose up -d --force-recreate web karena restart biasa mempertahankan nilai lama.

Apakah AGPL-3.0 melarang saya menawarkan ini kepada klien?

Tidak, tetapi lisensi tersebut menetapkan satu syarat. Jika Anda menjalankan kode tanpa modifikasi, Anda tidak memiliki kewajiban kepada siapa pun. Jika Anda memodifikasinya dan menjalankan versi modifikasi tersebut sebagai service yang digunakan orang lain, Anda harus menyediakan source yang telah dimodifikasi kepada pengguna tersebut. Public fork memenuhi kewajiban itu. Secara terpisah, nama "OpenAnalytics" tidak dilisensikan bersama kodenya. Jadi, apa pun yang Anda jual memerlukan nama sendiri.