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

Docker Compose di VPS Ubuntu 24.04

Pelajari instalasi Docker Engine dan Compose v2, contoh compose.yml dua service dengan PostgreSQL, jebakan port ufw, serta cara mencadangkan volume.

Yang Anda bangun

Docker Compose adalah fondasi untuk hampir semua hal lain di situs ini. Nextcloud, Vaultwarden, n8n, Immich, Rocket.Chat, setiap panduan tersebut diawali dengan “tulis file compose ini”, dan halaman ini menjelaskan arti sebenarnya dari file tersebut. Anda akan menginstal Docker Engine dan plugin Compose v2 dari repositori apt milik Docker pada Ubuntu 24.04, lalu menjalankan stack dua service yang nyata: Miniflux, pembaca RSS sederhana, serta PostgreSQL. Pasangan ini mencakup semua pola yang digunakan aplikasi yang lebih besar: image yang versinya dikunci, database dengan healthcheck, volume bernama, secret dalam file .env, serta port yang hanya dipublikasikan ke localhost.

Instalasi ini memerlukan waktu lima menit. Bagian selanjutnya membahas hal-hal yang dapat menimbulkan masalah di kemudian hari: docker group yang pada dasarnya adalah root dengan nama lain, port yang dipublikasikan dan melewati aturan ufw, serta satu flag pada docker compose down yang menghapus database tanpa meminta konfirmasi.

Prasyarat: KVM VPS Ubuntu 24.04 yang baru, user dengan akses sudo, dan RAM minimal 1 gigabyte. Instalasi Docker yang sudah ada juga tidak masalah; bagian pertama menjelaskan apa yang harus dihapus.

Instal dari repositori Docker, bukan repositori Ubuntu

Sebelum menjalankan perintah pertama, hindari dua pilihan yang keliru. Paket docker.io milik Ubuntu dapat digunakan, tetapi versinya tertinggal dari rilis Docker dan tidak memiliki tata letak plugin yang diasumsikan oleh komponen lain. Selain itu, biner docker-compose mandiri dengan tanda hubung adalah Compose v1: berbasis Python, tidak lagi didukung sejak 2023, dan menjadi penyebab tutorial lama gagal. Compose saat ini adalah docker compose dengan spasi, berupa plugin CLI yang diinstal dari repositori yang sama dengan engine.

Jika salah satu komponen tersebut sudah ada di server, hapus terlebih dahulu, termasuk docker-compose-v2, yaitu paket plugin milik Ubuntu, agar semua komponen berasal dari satu repositori:

sudo apt remove -y docker.io docker-compose docker-compose-v2 docker-doc podman-docker containerd runc

Package 'docker.io' is not installed, so not removed adalah keluaran normal pada VPS baru. Selanjutnya, tambahkan repositori Docker dan instal komponennya:

sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Verifikasi ketiga lapisan tersebut:

docker --version
docker compose version
sudo docker run --rm hello-world

Dua perintah pertama menampilkan string versi. Docker Compose version v2.x.x memastikan bahwa plugin yang terpasang adalah plugin, bukan biner v1 yang sudah tidak digunakan. Perintah hello-world seharusnya berakhir dengan Hello from Docker!. Paket tersebut mengaktifkan service saat boot. systemctl is-enabled docker menampilkan enabled.

Grup docker adalah root, jadi buat keputusan dengan memahami risikonya

Saat ini setiap perintah docker memerlukan sudo karena socket daemon di /var/run/docker.sock dimiliki oleh root dan grup docker. Tanpa keanggotaan tersebut, Anda akan mendapatkan error Docker yang paling sering dicari di Google:

permission denied while trying to connect to the Docker daemon socket at
unix:///var/run/docker.sock

Perbaikan standar:

sudo usermod -aG docker $USER

Keanggotaan grup berlaku saat login, sehingga error tetap muncul di shell saat ini. Jalankan newgrp docker untuk sesi ini, atau keluar lalu login kembali. Setelah itu, id seharusnya mencantumkan docker dalam grup Anda.

Berikut bagian pentingnya, secara langsung: keanggotaan dalam grup docker memberikan hak root pada host. Bukan sekadar hak yang mirip root atau hak yang ditingkatkan, melainkan root. Siapa pun yang berada dalam grup tersebut dapat menjalankan docker run --rm -it -v /:/host alpine chroot /host dan menguasai seluruh filesystem tanpa memerlukan kata sandi. Grup ini tersedia untuk kemudahan, bukan untuk membatasi akses.

Mode rootless Docker adalah alternatif yang sebenarnya. Dalam mode ini, daemon berjalan sebagai pengguna Anda yang tidak memiliki hak istimewa. Namun, ada konsekuensinya: port di bawah 1024 memerlukan konfigurasi tambahan, jaringan berjalan melalui shim userspace dengan overhead yang dapat diukur, dan beberapa image tidak berfungsi dengan baik tanpa root yang sebenarnya. Pada VPS dengan satu administrator, yang satu-satunya login-nya sudah memiliki sudo, perubahan grup ini secara praktik tidak mengubah apa pun. Mode ini digunakan oleh semua panduan di sini, tetapi jangan pernah memberikan akses tersebut seolah-olah haknya lebih rendah daripada sudo.

Anatomi file compose

Berikan setiap stack direktori sendiri. Nama direktori menjadi nama project yang menjadi awalan untuk container, network, dan volume:

sudo mkdir -p /opt/miniflux && sudo chown $USER /opt/miniflux && cd /opt/miniflux

Buat compose.yml (nama modernnya; docker-compose.yml masih berfungsi). Jangan gunakan key lama version: karena sudah usang dan Compose akan menampilkan peringatan jika menemukannya.

services:
  miniflux:
    image: miniflux/miniflux:2.2.9
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:8080"
    environment:
      - DATABASE_URL=postgres://miniflux:${POSTGRES_PASSWORD}@db/miniflux?sslmode=disable
      - RUN_MIGRATIONS=1
      - CREATE_ADMIN=1
      - ADMIN_USERNAME=admin
      - ADMIN_PASSWORD=${ADMIN_PASSWORD}
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      - POSTGRES_USER=miniflux
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=miniflux
    volumes:
      - db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "miniflux", "-d", "miniflux"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  db-data:

Setiap baris di atas merupakan keputusan. Bahas satu per satu.

Pin versi image, :latest ditambah pull berarti upgrade tanpa pengawasan

Gunakan postgres:16-alpine, bukan postgres:latest. Tag tidak bersifat tetap: :latest akan dipetakan ulang ke versi yang terakhir diunggah maintainer setiap kali Anda melakukan pull. Jika digabungkan dengan kebiasaan upgrade rutin yang akan Anda pelajari, yaitu docker compose pull && docker compose up -d, :latest berarti lompatan versi mayor terjadi setiap kali upstream merilisnya, bukan saat Anda memilihnya. Pada PostgreSQL, hal ini bukan kemungkinan teoretis: lompatan mendadak dari 16 ke 17 membuat container terus crash-loop karena data directory tidak kompatibel. Upgrade mayor Postgres memerlukan dump dan restore, bukan restart.

Setidaknya pin versi mayor (postgres:16-alpine mengikuti patch release 16.x), dan pin aplikasi ke release yang tepat seperti miniflux/miniflux:2.2.9. Periksa halaman releases project dan gunakan versi yang sedang berlaku saat Anda menulis file tersebut. Dengan demikian, upgrade menjadi perubahan satu baris yang Anda lakukan dengan sengaja dan terlihat di git diff.

Publish ke 127.0.0.1 karena Docker melewati ufw

"127.0.0.1:8080:8080", alamat host, port host, dan port container. Sebagian besar tutorial menulis "8080:8080", yang merupakan singkatan dari 0.0.0.0:8080:8080: mendengarkan pada semua interface, termasuk interface publik.

Di sinilah letak jebakannya, dan hampir semua orang pernah mengalaminya setidaknya sekali. Docker mem-publish port dengan menulis aturan DNAT yang mengubah tujuan paket ke IP internal container sebelum proses filtering. Akibatnya, paket melewati jalur FORWARD dan tidak pernah menyentuh INPUT, tempat aturan ufw Anda berada. sudo ufw deny 8080 melaporkan keberhasilan, ufw status menunjukkan bahwa port ditolak, tetapi service tetap menjawab seluruh Internet. Firewall Anda tidak rusak; berdasarkan desain, firewall tersebut dilewati. Mengapa Docker melewati ufw dan cara memfilter trafik container dengan benar menjelaskan mekanismenya serta perbaikan DOCKER-USER untuk port yang harus tetap publik.

Kebiasaan yang menghilangkan seluruh masalah ini adalah mengikat port yang dipublish ke 127.0.0.1, kecuali ada alasan khusus untuk tidak melakukannya. Gunakan reverse proxy di depannya untuk layanan yang harus menghadap Internet. Itulah yang dibangun oleh Panduan reverse proxy Traefik sebagai langkah berikutnya setelah halaman ini: satu container yang memiliki port 80 dan 443, lalu merutekan trafik ke layanan lain berdasarkan hostname, dengan TLS. (Jika Anda menggunakan setup Traefik v2 yang lebih lama, Panduan migrasi Traefik v2 ke v3 membahas perubahan nama dan aturan.)

Verifikasi binding setelah stack dijalankan: sudo ss -tlnp | grep 8080 harus menampilkan 127.0.0.1:8080, bukan 0.0.0.0:8080 atau *:8080.

Named volume vs bind mount

db-data:/var/lib/postgresql/data adalah named volume: Docker membuat dan mengelola direktori di bawah /var/lib/docker/volumes/, lalu me-mount-nya ke dalam container. Alternatifnya adalah bind mount, ./data:/var/lib/postgresql/data, yang memetakan path yang Anda pilih pada host.

Pembagian yang terbukti efektif dalam praktik adalah: gunakan named volume hanya untuk data yang disentuh oleh container, terutama database, karena Docker menginisialisasi volume dengan ownership yang diharapkan image dan permission file langsung berfungsi. Gunakan bind mount untuk file yang Anda sentuh dari host, seperti file konfigurasi yang diedit dengan text editor, media library yang Anda isi menggunakan rsync, atau data apa pun yang path-nya ingin Anda lihat dengan jelas. Kegagalan bind mount yang umum disebabkan oleh ownership: container berjalan sebagai UID 999, direktori pada host dimiliki UID 1000, dan aplikasi gagal saat start dengan permission denied di log-nya. Named volume hampir menghilangkan jenis masalah ini, dengan konsekuensi data berada di path yang dikelola Docker, seperti dijelaskan di bawah.

environment dan .env, jauhkan secret dari git

${POSTGRES_PASSWORD} tidak dibaca dari shell Anda. Compose melakukan interpolasi dari file bernama .env yang berada di sebelah compose.yml. Buat file tersebut:

cat > .env <<'EOF'
POSTGRES_PASSWORD=change-me-to-something-long
ADMIN_PASSWORD=change-me-too
EOF
chmod 600 .env
echo ".env" >> .gitignore

Buat nilai yang sebenarnya menggunakan openssl rand -hex 24. Gunakan hex, bukan base64, dengan alasan yang jelas: password ini akan berada di dalam connection string DATABASE_URL, dan karakter /, +, serta = yang dihasilkan base64 dapat merusak parsing URL. Kegagalan ini muncul sebagai error autentikasi, bukan error sintaks, dan dapat menghabiskan waktu satu malam. Baris .gitignore harus ditambahkan sebelum commit pertama: compose file aman untuk dipublikasikan dan diberi version control, sedangkan file .env tidak boleh dipublikasikan. Secret yang pernah masuk ke riwayat git harus dianggap sebagai secret yang perlu dirotasi. Jika Anda menjalankan stack dengan variabel yang tidak tersedia, Compose menampilkan peringatan dan melanjutkan dengan string kosong. Untuk password Postgres, hal ini menghasilkan deployment yang rusak:

WARN[0000] The "POSTGRES_PASSWORD" variable is not set. Defaulting to a blank string.

docker compose config mencetak file yang sudah diinterpolasi sepenuhnya. Ini adalah cara tercepat untuk memeriksa nilai yang benar-benar akan diterima container. Ingat, output-nya mencakup secret Anda.

depends_on tidak menunggu apa pun kecuali Anda menambahkan healthcheck

depends_on: [db] tanpa konfigurasi tambahan hanya mengatur urutan start: Compose menjalankan Postgres lebih dahulu, lalu menjalankan aplikasi sesaat kemudian, ketika Postgres masih memerlukan beberapa detik sebelum menerima koneksi. Aplikasi mencoba mengakses database, gagal, lalu crash atau melakukan retry, bergantung pada kualitas implementasinya.

Versi yang andal adalah konfigurasi yang digunakan file di atas: service db mendefinisikan healthcheck (Postgres menyediakan pg_isready khusus untuk tujuan ini), dan aplikasi mendeklarasikan depends_on dengan condition: service_healthy. Compose menjalankan database, memeriksa statusnya setiap 10 detik, lalu hanya menjalankan Miniflux setelah pemeriksaan berhasil. Jika database tidak pernah menjadi healthy, misalnya karena password salah atau volume rusak, aplikasi tidak akan dijalankan dan Compose memberi tahu dependency yang gagal:

dependency failed to start: container miniflux-db-1 is unhealthy

Pesan tersebut mengarahkan Anda ke docker compose logs db, tempat error sebenarnya berada.

restart: unless-stopped

restart: unless-stopped pada kedua service berarti container akan dijalankan kembali setelah crash dan setelah VPS reboot, tetapi tetap berhenti jika Anda sengaja menjalankan docker compose stop. Alternatif always akan menjalankan kembali container bahkan setelah penghentian manual, yang jarang sesuai dengan maksud Anda. Tanpa restart policy, reboot akibat kernel update pada pukul 4 pagi dapat menghentikan service Anda tanpa pemberitahuan sampai Anda menyadarinya.

Perintah harian

Semua kegiatan sehari-hari cukup menggunakan lima perintah yang dijalankan dari direktori project.

docker compose up -d        # create and start; idempotent, recreates only what changed
docker compose ps           # status, ports, and health of this project's containers
docker compose logs -f miniflux   # follow one service's logs; --tail 100 for recent history
docker compose pull && docker compose up -d   # upgrade to the pinned tags
docker compose down         # stop and remove containers and the network

up -d aman dijalankan berulang kali. Perintah ini membandingkan file dengan kondisi aktual dan hanya memproses ulang service yang konfigurasi atau image-nya berubah. Pasangan perintah upgrade mengambil apa pun yang kini ditunjuk oleh tag yang Anda pin: patch release di bawah postgres:16-alpine, dan tidak ada perubahan untuk pin yang persis sampai Anda mengeditnya. Itulah tujuannya. Image lama akan menumpuk setelah upgrade. Untuk membebaskan ruang disk, gunakan docker image prune -f.

Sekarang perintah yang bersifat destruktif, dan ini perlu ditegaskan: docker compose down aman karena container dan network dapat dibuat ulang, sedangkan data Anda berada di volume. docker compose down -v juga menghapus volume bernama. Itu berarti database Anda langsung hilang, tanpa prompt konfirmasi dan tanpa cara untuk membatalkannya. Flag -v digunakan untuk membongkar eksperimen. Pada stack yang menyimpan data nyata, perlakukan flag tersebut seperti rm -rf. Tidak ada tempat sampah di bawah /var/lib/docker/volumes/.

Untuk membuka shell satu kali di dalam container yang sedang berjalan: docker compose exec db psql -U miniflux membawa Anda ke database, sedangkan docker compose exec miniflux sh membuka shell pada app.

Lokasi sebenarnya data Anda

Named volume menggunakan awalan project. Karena itu, db-data dalam direktori bernama miniflux menjadi miniflux_db-data:

docker volume ls
docker volume inspect miniflux_db-data

Output inspect menyertakan baris yang penting:

"Mountpoint": "/var/lib/docker/volumes/miniflux_db-data/_data"

Direktori tersebut adalah database, dimiliki oleh root, berada pada sistem berkas host, dan tetap ada setelah down, upgrade, serta pembuatan ulang container. Direktori ini juga merupakan data yang harus dicadangkan oleh backup Anda.

Cadangkan volume bernama

Pola standar menggunakan container sementara yang memasang volume dalam mode hanya-baca di samping direktori host, lalu menjalankan tar untuk menyalin data:

docker run --rm \
  -v miniflux_db-data:/data:ro \
  -v "$PWD":/backup \
  alpine:3.22 tar czf /backup/miniflux-db-$(date +%F).tar.gz -C /data .

Tidak perlu melakukan instalasi dan tidak ada proses yang tetap berjalan. Pemulihan menggunakan pola kebalikan, yaitu tar xzf ke volume baru yang kosong dengan mount yang dibalik.

Ada satu hal yang perlu diperhatikan untuk database: menjalankan tar pada direktori data Postgres yang sedang berjalan dapat menangkap kondisi saat penulisan sedang berlangsung, sehingga database tidak dapat start dengan baik. Hentikan database selama beberapa detik yang diperlukan tar, yaitu docker compose stop, atau, lebih baik, buat logical dump yang secara bawaan konsisten:

docker compose exec -T db pg_dump -U miniflux miniflux | gzip > miniflux-$(date +%F).sql.gz

-T menonaktifkan pseudo-terminal yang secara default dialokasikan Compose. Menyalurkan output dump melalui TTY dapat merusaknya. Masukkan salah satu perintah ini ke cron dan salin hasilnya ke luar VPS. Backup pada disk yang sama dengan data yang dilindunginya hanyalah salinan, bukan backup. Panduan Nextcloud menyusun rutinitas terjadwal lengkap berdasarkan kedua pola ini.

Mode kegagalan dan string yang akan Anda lihat

permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock, Anda belum berada dalam grup docker, atau sudah berada di dalamnya tetapi sesi Anda dibuat sebelum perubahan tersebut. id menampilkan grup efektif Anda; newgrp docker memperbaiki shell saat ini, sedangkan logout lalu login kembali memperbaiki semuanya.

Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?, ini masalah yang berbeda: daemon itu sendiri sedang berhenti. sudo systemctl status docker dan sudo journalctl -u docker -n 50 menjelaskan penyebabnya. Pada VPS, penyebab yang umum adalah disk penuh. Jalankan df -h /var/lib/docker terlebih dahulu.

Bind for 127.0.0.1:8080 failed: port is already allocated, container lain sudah memublikasikan port host tersebut. docker ps menampilkan container yang menggunakannya; container lama dari eksperimen docker run beberapa minggu lalu biasanya menjadi penyebabnya. Jika docker ps tidak menampilkan apa pun, proses non-Docker sedang menggunakan port tersebut. sudo ss -tlnp | grep 8080 menampilkan prosesnya.

yaml: line 14: did not find expected key, terdapat kesalahan indentasi pada atau tepat di atas baris yang disebutkan. File Compose menggunakan YAML: gunakan indentasi dua spasi, spasi saja, dan karakter tab di mana pun akan menyebabkan kegagalan. docker compose config memvalidasi file tanpa menjalankan apa pun. Menjalankannya setelah setiap perubahan adalah kebiasaan sederhana yang bermanfaat.

Kejutan ufw tidak menampilkan error sama sekali. Inilah yang membuatnya berbahaya: deployment berhasil, ufw status terlihat benar, tetapi pemindaian port dari luar tetap menemukan database Anda. Baca kembali bagian port di atas, periksa setiap entri ports: untuk memastikan tidak ada awalan 127.0.0.1: yang hilang, lalu konfirmasi dari mesin yang berbeda dengan curl http://your-vps-ip:8080. Connection refused adalah hasil yang Anda inginkan.

Dari sini, panduan Traefik mengubah stack tunggal ini menjadi banyak aplikasi di balik satu entry point HTTPS, dan hal yang layak di-self-host pada 2026 adalah daftar aplikasi yang dapat Anda jalankan di baliknya. Setelah beberapa stack tersebut berjalan dan masing-masing menggunakan formulir login sendiri, server SSO self-hosted seperti Authentik menyatukannya kembali menjadi satu akun di balik proxy yang sama.

Game server seperti server Minecraft pada VPS merupakan proyek Compose awal yang baik untuk latihan. Jika Anda lebih suka mempelajari sesuatu yang dibuka setiap hari, openGym, pelacak latihan yang di-host sendiri adalah stack kecil yang dikunci ke tag git, bukan tag image. Stack ini memerlukan TLS di depannya sebelum Anda mendaftarkan passkey pertama. Foto biasanya menjadi hal pertama yang ingin dikembalikan orang dari cloud milik pihak lain. perbandingan PhotoPrism dan Immich membantu menentukan kebutuhan RAM minimum dan rutinitas pencadangan yang akan Anda gunakan sebelum menetapkan volume untuk salah satunya. Jika dua service mulai terasa tidak cukup, menyiapkan AFFiNE sebagai workspace bergaya Notion menerapkan pola yang sama pada empat container. Ini juga menjadi uji yang baik untuk melihat apakah tag yang dikunci, healthcheck, dan named volume di atas sudah menjadi kebiasaan.

FAQ

Mengapa saya mendapatkan "permission denied while trying to connect to the Docker daemon socket"?

Pengguna Anda tidak berada dalam grup docker, atau ditambahkan setelah sesi saat ini dimulai. Keanggotaan grup hanya berlaku saat login. Jalankan sudo usermod -aG docker $USER, lalu newgrp docker atau logout dan login kembali, kemudian konfirmasikan dengan id. Grup ini memberikan akses yang setara dengan root ke host. Karena itu, hanya tambahkan pengguna yang juga akan Anda beri akses sudo.

Apakah docker compose down menghapus data saya?

docker compose down biasa tidak menghapus data. Perintah ini menghapus container dan jaringan project. Named volume tetap ada, lalu up -d berikutnya akan menggunakannya kembali. docker compose down -v adalah bentuk yang destruktif. Perintah ini menghapus named volume, termasuk database Anda, tanpa konfirmasi dan tanpa opsi pemulihan. Jangan pernah menjalankan -v pada stack yang berisi data nyata, kecuali Anda memiliki backup yang sudah diverifikasi.

Apa perbedaan antara docker-compose dan docker compose?

docker-compose (dengan tanda hubung) adalah Compose v1, yaitu binary Python mandiri yang mencapai akhir masa dukungan pada 2023 dan sebaiknya tidak diinstal pada server baru. docker compose (dengan spasi) adalah Compose v2, yaitu plugin Go untuk Docker CLI, yang diinstal sebagai docker-compose-plugin dari repositori apt Docker. Perintah dan YAML hampir sepenuhnya kompatibel. Jadi, jika tutorial lama menyebut docker-compose up, ketik docker compose up.

Mengapa saya dapat mengakses container Docker dari Internet meskipun ufw memblokir port tersebut?

Karena Docker memublikasikan port menggunakan aturan DNAT dalam chain PREROUTING milik iptables. Paket yang telah ditulis ulang melewati jalur FORWARD melalui chain milik Docker sendiri. Paket tersebut tidak pernah mencapai chain INPUT, tempat aturan ufw diterapkan. Karena itu, ufw deny 8080 tidak berpengaruh pada port container yang dipublikasikan. Perbaiki dari sumbernya: publikasikan ke 127.0.0.1: dan sediakan akses ke service melalui reverse proxy.

Sebaiknya saya menggunakan named volume atau bind mount?

Gunakan named volume untuk data yang hanya diakses oleh container, terutama database, karena Docker menetapkan ownership sesuai dengan yang diharapkan image dan izin biasanya langsung berfungsi. Gunakan bind mount untuk file yang juga Anda kelola dari host: konfigurasi yang Anda edit, media yang Anda unggah, dan apa pun yang path-nya ingin Anda buat jelas. Jika container gagal saat start dengan permission denied pada bind mount, hal pertama yang perlu diperiksa adalah ketidaksesuaian UID antara host dan container.