SSD Nodes Learn 🎉 VPS mulai $5.50/bln
Panduan Matt ConnorOleh Matt Connor · Diperbarui 2026-08-13

Cara Menjalankan Mealie di VPS dengan Docker Compose

Jalankan Mealie di VPS sendiri memakai Docker Compose. Tempel tautan resep untuk mengambil bahan dan langkah, lalu gunakan rencana menu, daftar belanja, nginx, TLS, dan backup.

Fungsi recipe manager yang di-host sendiri

Recipe manager yang di-host sendiri menyimpan resep Anda dalam database pada server milik Anda, dan Mealie adalah pilihan yang paling banyak digunakan oleh rumah tangga. Anda menempelkan alamat halaman resep, lalu Mealie membaca bahan, langkah, jumlah porsi, dan waktu memasak dari halaman tersebut. Cerita dan iklannya tidak ikut disimpan. Yang masuk ke koleksi Anda hanya resepnya.

Fitur lain dalam aplikasi ini sederhana. Tersedia rencana menu mingguan untuk menambahkan resep dengan metode drag-and-drop, serta daftar belanja yang dibuat berdasarkan rencana tersebut. Setiap orang yang memasak dapat memiliki login sendiri. Seluruh aplikasi berjalan dalam satu container dan hampir tidak menggunakan sumber daya saat tidak ada permintaan. VPS dengan spesifikasi sederhana dapat menjalankannya tanpa beban berarti.

Panduan ini menggunakan Docker Compose. Jika istilah services: dan volumes: masih baru bagi Anda, baca cara menyusun file Docker Compose terlebih dahulu. Semua langkah di bawah ini hanya terdiri atas satu file compose dan empat perintah.

Instal Mealie dengan Docker Compose

Mealie menerbitkan image-nya ke GitHub container registry. Per Juli 2026, tag stabil saat ini adalah v3.22.0. Tetapkan versinya, jangan gunakan latest: dengan latest, sebuah docker compose pull pada hari yang tidak terkait dapat membawa Anda melewati migrasi database yang belum siap Anda jalankan.

sudo mkdir -p /srv/mealie
cd /srv/mealie
sudo nano docker-compose.yml
services:
  mealie:
    image: ghcr.io/mealie-recipes/mealie:v3.22.0
    container_name: mealie
    restart: always
    ports:
      - "127.0.0.1:9925:9000"
    deploy:
      resources:
        limits:
          memory: 1000M
    volumes:
      - mealie-data:/app/data/
    environment:
      ALLOW_SIGNUP: "false"
      PUID: 1000
      PGID: 1000
      TZ: Europe/Amsterdam
      BASE_URL: https://recipes.example.com

volumes:
  mealie-data:

Periksa dua baris berikut sebelum menjalankannya.

Port ditulis sebagai 127.0.0.1:9925:9000, bukan 9925:9000. Container mendengarkan pada port 9000 di dalam container, sedangkan host memetakan port 9925 ke port tersebut. Mengikat pemetaan itu ke alamat loopback memungkinkan nginx menjangkau Mealie, tetapi mencegah akses dari Internet. Docker menulis aturan sendiri ke packet filter, sehingga 9925:9000 biasa tetap dapat diakses dari luar meskipun firewall Anda menyatakan bahwa port tersebut tertutup. Hal ini perlu dipahami sekali: lihat alasan port Docker yang dipublikasikan mengabaikan ufw.

BASE_URL harus berupa alamat publik persis yang akan Anda gunakan, lengkap dengan skema dan tanpa garis miring di akhir. Mealie membuat tautan pengaturan ulang kata sandi dan tautan undangan berdasarkan alamat tersebut. Jika Anda menetapkannya ke http://localhost:9925, undangan yang Anda kirim kepada pasangan Anda akan berisi tautan yang hanya berfungsi di server itu sendiri.

Jalankan dan pantau proses boot pertama.

sudo docker compose up -d
sudo docker compose logs -f mealie

Saat pertama kali dijalankan, container membuat database SQLite dan menjalankan migrasi. Proses ini memerlukan beberapa detik. Setelah log stabil dan tidak lagi menampilkan baris migrasi, periksa aplikasi secara lokal.

curl -I http://127.0.0.1:9925

Respons 200 OK berarti aplikasi sudah berjalan. Respons Connection refused berarti container tidak sedang berjalan: jalankan sudo docker compose ps dan baca exit code-nya. Container yang berhenti dengan kode 137 telah dihentikan karena melampaui batas memori 1000M. Hal ini dapat terjadi pada paket dengan sumber daya paling kecil.

Login pertama dan menonaktifkan pendaftaran terbuka

Akun default adalah changeme@example.com dengan password MyPassword. Login menggunakan akun tersebut, lalu segera ubah keduanya karena pasangan ini tercantum dalam dokumentasi dan karenanya ada di setiap scanner.

ALLOW_SIGNUP: "false" dalam file compose memang disengaja. Jika pendaftaran terbuka, siapa pun yang menemukan alamat tersebut dapat membuat akun di kotak resep Anda. Jika pendaftaran ditutup, Anda menambahkan pengguna dari area admin. Sistem akan membuat link undangan yang Anda kirim sendiri. Link tersebut dibuat berdasarkan BASE_URL. Karena itu, nilainya penting. Jika Anda menjalankan beberapa aplikasi pada server yang sama dan ingin menggunakan satu password untuk semuanya, Mealie dapat menyerahkan proses login kepada identity provider eksternal, seperti instance Authentik yang di-host sendiri.

Mealie mengelompokkan pengguna ke dalam satu household. Semua orang dalam household yang sama berbagi koleksi resep, meal plan, dan shopping list. Ini sesuai untuk keluarga. Household yang terpisah pada server yang sama memiliki koleksi terpisah. Ini sesuai untuk hunian bersama ketika tidak ada yang sepakat tentang ikan teri.

Importer, alasan utama menjalankan ini

Buka koleksi resep, pilih untuk membuat resep dari URL, lalu tempelkan tautan. Mealie mengambil halaman tersebut dan mencari data resep terstruktur, yaitu blok yang dapat dibaca mesin dan biasanya disematkan situs resep untuk mesin pencari. Jika blok tersebut tersedia, proses import berlangsung bersih dan segera.

Anda juga dapat mengimpor dari gambar atau teks biasa yang ditempelkan, termasuk foto halaman dari buku resep. Sumber tersebut diproses melalui jalur yang lebih lambat dan perlu diperiksa setelahnya karena pecahan tulisan tangan mudah salah dibaca.

Import massal dijalankan dari layar yang sama: tempelkan daftar alamat, satu alamat per baris, lalu Mealie memprosesnya di latar belakang. Koleksi berisi dua ratus bookmark dapat dipindahkan dalam satu kali proses.

Rencana makan dan daftar belanja

Perencana makan adalah kalender. Seret resep ke suatu hari untuk merencanakannya. Daftar belanja kemudian mengumpulkan bahan-bahan dari resep yang direncanakan ke dalam satu daftar dan menggabungkan item yang sama. Jadi, dua resep yang membutuhkan bawang menghasilkan satu baris, bukan dua.

Daftar tersebut adalah halaman aktif yang dapat dibuka di ponsel saat berbelanja. Karena server Anda sendiri yang menyimpannya, semua anggota rumah tangga melihat daftar yang sama secara bersamaan. Saat seseorang menandai susu sebagai sudah dibeli, item tersebut juga hilang dari layar orang lain.

Letakkan nginx dan TLS di depan

Mealie menggunakan HTTP biasa dan tidak menangani sertifikat sendiri. Hentikan TLS pada nginx yang ditempatkan di depannya. Arahkan record DNS A ke server Anda terlebih dahulu karena proses penerbitan sertifikat memverifikasi nama tersebut.

sudo apt update && sudo apt install -y nginx
sudo nano /etc/nginx/sites-available/mealie
server {
    listen 80;
    server_name recipes.example.com;

    client_max_body_size 64M;

    location / {
        proxy_pass http://127.0.0.1:9925;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
sudo ln -s /etc/nginx/sites-available/mealie /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

nginx -t yang menampilkan syntax is ok dan test is successful adalah pemeriksaan penentu. Lakukan reload hanya setelah pemeriksaan tersebut berhasil karena reload konfigurasi yang rusak akan mempertahankan konfigurasi lama dan menyembunyikan kesalahan hingga restart berikutnya.

client_max_body_size 64M diperlukan karena nilai default nginx adalah 1 MB. Mengunggah foto resep atau memulihkan cadangan melalui browser mengirimkan body yang lebih besar dari nilai tersebut. Tanpa baris itu, nginx mengembalikan 413 Request Entity Too Large, bukan Mealie, sehingga log aplikasi sama sekali tidak menampilkan apa pun.

Selanjutnya, terbitkan sertifikat. Langkah tersebut dan timer pembaruannya dibahas dalam menerbitkan sertifikat Let's Encrypt untuk nginx dengan certbot.

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d recipes.example.com

Certbot menulis ulang server block agar listen pada 443 dan menambahkan redirect dari port 80. Buka situs melalui https:// dan pastikan browser menerima sertifikat tersebut. Jika Mealie dimuat tetapi tautannya sendiri mengarahkan Anda ke http://, berarti BASE_URL masih berisi http dan perlu diperbaiki. Setelah itu, jalankan sudo docker compose up -d untuk membuat ulang container dengan nilai baru.

Menjalankan Mealie di bawah subpath seperti example.com/recipes tidak berfungsi karena frontend tidak dapat disajikan dari subpath. Gunakan subdomain.

Cadangan, dan apa yang sebenarnya dilakukan proses pemulihan

Semua data yang dikelola Mealie berada di /app/data/ di dalam container, yaitu volume mealie-data. Salin volume tersebut untuk menyalin resep, gambar, dan database sekaligus.

sudo docker volume ls
sudo docker compose stop mealie
sudo docker run --rm -v mealie_mealie-data:/data -v "$PWD":/backup \
  alpine tar czf /backup/mealie-data.tgz -C /data .
sudo docker compose start mealie

Nama volume menggunakan nama proyek sebagai awalan. Nama proyek adalah direktori yang berisi file compose. Dari /srv/mealie, volume tersebut adalah mealie_mealie-data. Karena itu, perintah pertama adalah docker volume ls: gunakan nama yang ditampilkan oleh perintah tersebut, bukan nama dalam panduan ini. Container harus dihentikan terlebih dahulu. SQLite cukup sering sedang menulis data, sehingga salinan saat container aktif dapat menghasilkan data yang tidak dapat dibaca saat dipulihkan.

Mealie juga memiliki halaman pencadangan sendiri di area admin. Halaman ini membuat arsip portabel yang berisi database dalam format JSON beserta gambar Anda. Gunakan fitur ini saat memindahkan data antarserver. Arsip tersebut tetap dapat digunakan setelah perubahan versi, sedangkan penyalinan file mentah belum tentu demikian. Pemulihan arsip bersifat destruktif. Proses ini menghapus database saat ini sebelum memuat arsip, dan tidak dapat dibatalkan. Anda akan keluar dari sesi setelah proses selesai.

Tidak satu pun dari kedua salinan tersebut merupakan cadangan jika masih berada di server yang sama. Kirim arsip ke lokasi lain secara terjadwal. Itulah fungsi cadangan terenkripsi di luar server dengan restic.

Memperbarui Mealie

cd /srv/mealie
sudo nano docker-compose.yml
sudo docker compose pull
sudo docker compose up -d
sudo docker compose logs -f mealie

Naikkan versi yang dipatok di dalam file, lalu pull dan buat ulang container. Migrasi berjalan saat image baru pertama kali dijalankan. Salin volume sebelum mengubah versi mayor, karena migrasi yang gagal di tengah proses dapat meninggalkan database yang tidak lagi dapat dibuka oleh image sebelumnya. Baca catatan rilis untuk semua perubahan antara versi Anda dan versi baru.

Saat pengimporan gagal

Sebagian situs tidak menyediakan data resep terstruktur sama sekali. Mealie kemudian mengimpor judul tanpa daftar bahan. Hal ini tidak dapat diatasi dengan mengubah konfigurasi. Sebagai gantinya, tempelkan teks resep secara manual.

Kegagalan lain disebabkan oleh perlindungan bot di depan situs resep. Perlindungan tersebut mengirim halaman tantangan kepada Mealie, bukan resepnya. Mealie sudah menyamarkan identitasnya sebagai browser dan mengganti user agent secara berkala untuk mengurangi masalah ini. Jika situs tetap menolak permintaan, opsi yang didokumentasikan adalah meneruskan scraper melalui proxy dengan reputasi alamat yang lebih baik, atau menjalankan instance FlareSolverr yang menyelesaikan tantangan menggunakan browser sungguhan. Keduanya bersifat opsional dan dikonfigurasi melalui environment variable pada container.

Pengimporan yang gagal karena server Anda sama sekali tidak dapat menjangkau situs tersebut merupakan masalah yang berbeda. Uji dari server dengan curl -I https://the-site.example/recipe dan baca baris statusnya sebelum menyalahkan scraper.

Kesesuaiannya

Mealie adalah aplikasi self-hosting pertama yang baik untuk rumah tangga karena orang yang tinggal bersama Anda akan menggunakannya tanpa perlu diminta. Aplikasi ini memiliki jenis penggunaan yang serupa dengan menjalankan pustaka foto sendiri menggunakan Immich, tetapi jauh lebih ringan. Mealie juga termasuk dalam daftar hal-hal yang layak di-self-host tahun ini yang lebih luas. Satu server kecil dapat menjalankan keduanya. Immich bukan satu-satunya kandidat untuk kebutuhan kedua tersebut. Jika Anda masih menentukan pilihan, batas minimum memori dan perintah backup PhotoPrism serta Immich cukup berbeda sehingga layak dibaca sebelum Anda mengalokasikan sisa ruang disk.

FAQ

Mengapa impor URL resep gagal?

Ada dua penyebab umum. Halaman tersebut tidak menyediakan data resep terstruktur, sehingga scraper tidak menemukan apa pun dan Anda hanya mendapatkan judul tanpa bahan, atau lapisan perlindungan bot di depan situs mengembalikan halaman tantangan, bukan resepnya. Untuk kasus kedua, Mealie dapat diarahkan ke proxy dengan reputasi alamat yang lebih baik, atau ke instance FlareSolverr yang di-host sendiri dan menyelesaikan tantangan melalui browser sungguhan. Pastikan server Anda dapat menjangkau halaman tersebut sama sekali dengan curl -I sebelum mengubah apa pun.

Apakah saya memerlukan PostgreSQL, atau SQLite sudah cukup?

SQLite cukup untuk rumah tangga dan merupakan pilihan default. Gunakan PostgreSQL jika direktori data berada pada penyimpanan terpasang jaringan, karena SQLite pada sistem berkas jaringan dapat menghasilkan error database terkunci dan merusak file. Pemulihan dengan PostgreSQL memerlukan pengguna database sebagai superuser, karena proses pemulihan menghapus semuanya sebelum memuat arsip.

Apakah saya dapat menjalankan Mealie tanpa nama domain?

Ya, pada jaringan Anda sendiri. Atur BASE_URL ke alamat yang benar-benar akan Anda ketik, seperti http://192.168.1.20:9925, dan jangan gunakan nginx. Tautan undangan dan pengaturan ulang kata sandi dibuat dari BASE_URL, sehingga nilai yang salah menghasilkan tautan yang tidak dapat dibuka orang lain. Jangan mengeksposnya ke Internet melalui HTTP biasa, karena login akan dikirim tanpa enkripsi.

Bagaimana cara memberi keluarga saya login masing-masing?

Biarkan ALLOW_SIGNUP tetap disetel ke "false", lalu tambahkan orang dari area admin. Tindakan ini menghasilkan tautan undangan yang dapat Anda kirimkan kepada mereka. Masukkan semua orang yang menggunakan dapur yang sama ke dalam household yang sama agar mereka berbagi resep, rencana makanan, dan daftar belanja. Household yang terpisah pada satu server akan mempertahankan koleksi yang terpisah.

Apa yang terjadi pada resep saya jika saya berhenti menjalankan Mealie?

Resep tersebut tetap dapat Anda ambil. Backup admin menulis data Anda sebagai JSON, dan Mealie juga dapat mengekspor resep sebagai file markdown biasa yang tetap dapat dibaca di editor teks apa pun tanpa perangkat lunak khusus. Buat satu ekspor sebelum Anda membutuhkannya dan pastikan file tersebut dapat dibuka.