SSD Nodes Learn Hosting plans →
Mga Gabay Matt ConnorNi Matt Connor · Na-update 2026-08-27

Docker Compose sa VPS: Ubuntu 24.04 Setup Guide

I-install ang Docker Engine at Compose v2 sa Ubuntu 24.04, gumawa ng Miniflux at PostgreSQL stack, iwasan ang ufw port trap, at mag-back up ng volumes.

Mga itinatayo mo

Ang Docker Compose ang base layer ng halos lahat ng iba pa sa site na ito. Nextcloud, Vaultwarden, n8n, Immich, Rocket.Chat—lahat ng gabay na iyon ay nagsisimula sa “isulat ang compose file na ito,” at ipinapaliwanag ng page na ito kung ano talaga ang ibig sabihin ng file na iyon. I-i-install mo ang Docker Engine at ang Compose v2 plugin mula sa sariling apt repository ng Docker sa Ubuntu 24.04. Pagkatapos, magse-set up ka ng aktuwal na two-service stack: Miniflux, isang maliit na RSS reader, at PostgreSQL. Ginagamit ng pares na ito ang lahat ng pattern na ginagamit ng mas malalaking app: pinned images, database na may healthcheck, named volume, secrets sa isang .env file, at port na naka-publish lamang sa localhost.

Limang minuto ang kailangan para sa installation. Tatalakayin ng natitirang bahagi ng gabay ang mga problemang karaniwang lumilitaw sa paglaon: ang docker group na root sa ibang pangalan lamang, ang published ports na direktang lumalampas sa iyong ufw rules, at ang isang flag sa docker compose down na nagde-delete ng iyong database nang walang confirmation prompt.

Mga kinakailangan: isang bagong Ubuntu 24.04 KVM VPS, user na may sudo, at 1 gigabyte ng RAM o higit pa. Ayos din kung mayroon nang Docker installation; tatalakayin sa unang seksyon ang mga dapat alisin.

Mag-install mula sa Docker repository, hindi sa Ubuntu

Dalawang maling pagpipilian ang dapat iwasan bago patakbuhin ang unang command. Gumagana ang sariling docker.io package ng Ubuntu, pero nahuhuli ito sa mga release ng Docker at wala rito ang plugin layout na inaasahan ng ibang bahagi ng setup. Ang standalone na docker-compose binary naman, ang may hyphen, ay Compose v1: nakasulat sa Python, end-of-life na mula 2023, at dahilan kung bakit hindi na gumagana ang mga lumang tutorial. Ang Compose ngayon ay docker compose na may space, isang CLI plugin na ini-install mula sa parehong repository ng engine.

Kung mayroon na sa server ang alinman sa mga ito, alisin muna ang mga ito, pati ang docker-compose-v2, na sariling packaging ng Ubuntu para sa plugin, upang mula sa iisang repository manggaling ang lahat:

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 ang karaniwang output sa bagong VPS. Pagkatapos, idagdag ang repository ng Docker at i-install ito:

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

I-verify ang tatlong layer:

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

Ang unang dalawang command ay nagpi-print ng mga version string. Kinukumpirma ng Docker Compose version v2.x.x na mayroon kang plugin at hindi ang hindi na ginagamit na v1 binary. Dapat magtapos sa Hello from Docker! ang output ng hello-world run. Ine-enable ng package ang service sa boot; nagpi-print ang systemctl is-enabled docker ng enabled.

Ang docker group ay root, kaya magpasya nang malinaw

Sa ngayon, kailangan ng docker ang sudo dahil ang daemon socket sa /var/run/docker.sock ay pagmamay-ari ng root at ng docker group. Kung wala kang membership, makikita mo ang pinakakaraniwang Docker error na hinahanap online:

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

Ang standard na pag-aayos:

sudo usermod -aG docker $USER

Naa-apply ang group membership sa pag-login, kaya mananatili ang error sa kasalukuyan mong shell. Patakbuhin ang newgrp docker para sa session na ito, o mag-log out at mag-log in muli; dapat nang ilista ng id ang docker sa iyong mga group.

Narito ang mahalagang paliwanag: ang membership sa docker group ay katumbas ng root access sa host. Hindi ito "halos root", hindi ito "elevated" lamang—root ito. Maaaring patakbuhin ng sinumang kabilang sa group na iyon ang docker run --rm -it -v /:/host alpine chroot /host at magkaroon ng ganap na kontrol sa buong filesystem, nang walang hinihinging password. Para sa convenience ang group na ito, hindi para sa containment.

Ang rootless mode ng Docker ang tunay na alternatibo; ang daemon mismo ay tumatakbo bilang iyong unprivileged user. May kapalit ito: kailangan ng karagdagang setup para sa mga port na mas mababa sa 1024, dumadaan ang networking sa userspace shim na may nasusukat na overhead, at may ilang image na hindi gumagana nang maayos nang walang aktuwal na root. Sa isang single-admin VPS kung saan ang tanging login ay mayroon nang sudo, walang praktikal na pagbabago ang paggamit ng group, at ito ang ipinapalagay ng lahat ng guide dito. Gayunman, huwag itong ibigay na para bang mas mababa ang pribilehiyo nito kaysa sudo.

Anatomy ng compose file

Bigyan ang bawat stack ng sarili nitong directory. Ang pangalan ng directory ang nagiging project name na nagiging prefix ng mga container, network, at volume:

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

Gumawa ng compose.yml (ito ang modernong pangalan; gumagana pa rin ang docker-compose.yml). Huwag gamitin ang lumang version: key dahil obsolete na ito, at magbibigay ng warning ang Compose kapag nakita ito.

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:

Bawat linya sa itaas ay isang desisyon. Isa-isahin ang mga ito.

I-pin ang mga image version; ang :latest na sinabayan ng pull ay unattended upgrade

postgres:16-alpine, hindi postgres:latest. Hindi frozen ang isang tag: bawat pag-pull, muling nireresolba ang :latest sa pinakabagong version na itinulak ng maintainer. Kapag isinabay ito sa routine upgrade habit na matututunan mo, docker compose pull && docker compose up -d, ibig sabihin ng :latest ay dumarating ang mga major-version jump kapag nire-release ang mga ito upstream, hindi kapag pinili mo. Hindi hypothetical ito sa PostgreSQL: maaaring mag-crash-loop ang container dahil sa biglaang jump mula 16 patungong 17 at sa incompatible na data directory, dahil nangangailangan ang Postgres ng dump at restore para sa major upgrade, hindi simpleng restart.

I-pin man lang ang major version (sinusundan ng postgres:16-alpine ang mga patch release ng 16.x), at i-pin ang mga application sa eksaktong release gaya ng miniflux/miniflux:2.2.9. Suriin ang releases page ng project at gamitin ang kasalukuyang version kapag isinusulat mo ang file. Ang upgrade ay magiging one-line edit na sinadya mong gawin at makikita sa git diff.

I-publish sa 127.0.0.1 dahil nilalampasan ng Docker ang ufw

"127.0.0.1:8080:8080": host address, host port, container port. Kadalasang isinusulat ng mga tutorial ang "8080:8080", na shorthand para sa 0.0.0.0:8080:8080: nakikinig ito sa lahat ng interface, kabilang ang public interface.

Narito ang karaniwang trap, at halos lahat ay tinatamaan nito kahit isang beses. Nagpa-publish ang Docker ng port sa pamamagitan ng pagsusulat ng DNAT rule na nagre-rewrite sa destination ng packet patungo sa internal IP ng container bago ang filtering. Dahil dito, dumaraan ang packet sa FORWARD path at hindi nito naaabot ang INPUT, kung saan naroon ang iyong ufw rules. Nag-uulat ang sudo ufw deny 8080 ng tagumpay, ipinapakita ng ufw status na denied ang port, pero sumasagot pa rin ang service sa buong internet. Hindi sira ang firewall mo; nilalampasan ito ng Docker ayon sa disenyo. Ipinapaliwanag ng Bakit nilalampasan ng Docker ang ufw at kung paano talagang i-filter ang container traffic ang mekanismo at ang DOCKER-USER fix para sa mga port na kailangang manatiling public.

Ang habit na tuluyang nag-aalis sa problemang ito: i-bind ang mga published port sa 127.0.0.1 maliban kung may partikular kang dahilan para hindi gawin ito, at maglagay ng reverse proxy sa harap ng anumang service na dapat ma-access ng mundo. Ito mismo ang ginagawa ng Traefik reverse proxy guide bilang susunod na hakbang pagkatapos ng page na ito: isang container na may hawak ng ports 80 at 443 at nagra-route sa lahat ng iba pang service batay sa hostname, gamit ang TLS. (Kung mula ka sa lumang Traefik v2 setup, tinatalakay ng Traefik v2 hanggang v3 migration guide ang mga rename at pagbabago sa rules.)

I-verify ang bind pagkatapos simulan ang stack: dapat ipakita ng sudo ss -tlnp | grep 8080 ang 127.0.0.1:8080, hindi ang 0.0.0.0:8080 o *:8080.

Named volumes kumpara sa bind mounts

Ang db-data:/var/lib/postgresql/data ay isang named volume: gumagawa at mina-manage ng Docker ang directory sa ilalim ng /var/lib/docker/volumes/ at mina-mount ito sa container. Ang alternatibo ay bind mount, ./data:/var/lib/postgresql/data, na nagma-map sa path na pinili mo sa host.

Ang paghahati na maaasahan sa aktuwal na paggamit: named volumes para sa data na container lamang ang humahawak, lalo na ang mga database, dahil ini-initialize ng Docker ang volume gamit ang ownership na inaasahan ng image at awtomatikong gumagana ang file permissions. Bind mounts para sa mga file na hinahawakan mo mula sa host gaya ng config files na ine-edit gamit ang text editor, media library na sini-sync mo gamit ang rsync, at anumang path na gusto mong madaling makita. Ang karaniwang problema sa bind mount ay ownership: tumatakbo ang container bilang UID 999, pagmamay-ari ng UID 1000 ang directory sa host, at namamatay ang app sa startup na may permission denied sa logs nito. Kadalasang inaalis ng named volumes ang ganitong uri ng bug, kapalit ng pag-iimbak ng data sa Docker-managed path na tatalakayin sa ibaba.

environment at .env, ilayo ang secrets sa git

Hindi binabasa ang ${POSTGRES_PASSWORD} mula sa shell mo. Ini-interpolate ito ng Compose mula sa file na pinangalanang .env at nasa tabi ng compose.yml. Gawin ito:

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

Gumamit ng openssl rand -hex 24 para bumuo ng totoong values. Hex, hindi base64, ang ginagamit dito dahil napupunta ang password na ito sa loob ng DATABASE_URL connection string. Sinisira ng /, +, at = characters na ginagawa ng base64 ang URL parsing. Lumalabas ang failure bilang authentication error, hindi syntax error, at maaaring umubos ng isang gabi sa pag-debug. Ilagay ang .gitignore line bago ang unang commit: ligtas i-publish at i-version ang compose file, pero hindi kailanman ang .env file. Kapag napunta sa git history ang isang secret, kailangan mo itong i-rotate. Kapag sinimulan mo ang stack na may nawawalang variable, magbibigay ng malakas na warning ang Compose at magpapatuloy gamit ang empty string. Para sa Postgres password, nagreresulta ito sa sirang deployment:

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

Ipinapakita ng docker compose config ang fully interpolated file. Ito ang pinakamabilis na paraan para tingnan kung ano talaga ang matatanggap ng mga container. Tandaan na kasama sa output nito ang iyong secrets.

Ang depends_on ay walang hinihintay maliban kung magdagdag ka ng healthcheck

Ang simpleng depends_on: [db] ay kumokontrol lamang sa order ng pagsisimula. Inuuna ng Compose na ilunsad ang Postgres at ilang sandali pagkatapos ay ang app, habang ilang segundo pa bago tumanggap ng connections ang Postgres. Susubukan ng app na kumonekta sa database, mabibigo, at magka-crash o magre-retry depende sa kalidad ng pagkakasulat dito.

Ang maaasahang bersyon ang ginagamit ng file sa itaas: nagde-define ang db service ng healthcheck (may pg_isready ang Postgres para mismo rito), at nagdedeklara ang app ng depends_on gamit ang condition: service_healthy. Sinisimulan ng Compose ang database, sinusuri ito bawat 10 seconds, at saka lamang sinisimulan ang Miniflux kapag pumasa ang check. Kung hindi kailanman magiging healthy ang database dahil sa maling password o corrupt na volume, hindi sisimulan ang app at sasabihin ng Compose kung aling dependency ang nabigo:

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

Itinuturo ng mensaheng iyon ang docker compose logs db, kung saan makikita ang aktuwal na error.

restart: unless-stopped

Ang restart: unless-stopped sa dalawang service ay nangangahulugang babalik ang mga container pagkatapos ng crash at pagkatapos ng VPS reboot, pero mananatiling down ang mga ito kung sinadya mong patakbuhin ang docker compose stop. Binubuhay muli ng alternatibong always ang mga container kahit pagkatapos ng manual stop, na bihirang siyang tunay na kailangan mo. Kung walang restart policy, maaaring tahimik na ma-down ang mga service dahil sa reboot para sa kernel update nang 4 a.m. hanggang sa mapansin mo ito.

Ang mga pang-araw-araw na command

Limang command lang ang kailangan para sa mga karaniwang gawain. Patakbuhin ang mga ito mula sa project directory.

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

Ligtas ulit-ulitin ang up -d. Inihahambing nito ang file sa aktuwal na estado at ina-update lamang ang mga service na nagbago ang configuration o image. Kinukuha ng upgrade pair ang anumang pinned tag na kasalukuyang tinutukoy: mga patch release sa ilalim ng postgres:16-alpine. Walang makukuha para sa exact pin hangga't hindi mo ito ine-edit, at iyon ang layunin. Naiipon ang mga lumang image pagkatapos ng mga upgrade. Bawiin ang disk space gamit ang docker image prune -f.

Ngayon ang destructive command. Bigyang-diin natin ito: ligtas ang docker compose down; disposable ang mga container at network, at nasa volume ang iyong data. Tinatanggal din ng docker compose down -v ang mga named volume. Ibig sabihin, mawawala agad ang database nang walang confirmation prompt at walang paraan para i-undo. Para sa pag-tear down ng mga experiment ang -v flag. Kung may totoong data ang stack, ituring ito gaya ng pagtrato mo sa rm -rf. Walang trash can sa ilalim ng /var/lib/docker/volumes/.

Para sa one-off shell sa loob ng tumatakbong container: inilalagay ka ng docker compose exec db psql -U miniflux sa database, at binibigyan ka ng docker compose exec miniflux sh ng shell sa app.

Kung saan talaga nakalagay ang iyong data

Nagtatanggap ng project prefix ang mga named volume, kaya nagiging miniflux_db-data ang db-data sa isang directory na tinatawag na miniflux:

docker volume ls
docker volume inspect miniflux_db-data

Kasama sa output ng inspect ang mahalagang line:

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

Ang directory na iyon ang database, root-owned sa host filesystem, at nananatili ito pagkatapos ng down, upgrades, at mga rebuild ng container. Ito rin mismo ang kailangang isama ng iyong backups.

Mag-backup ng named volume

Ang karaniwang pattern ay isang pansamantalang container na nagmo-mount sa volume bilang read-only sa tabi ng isang host directory, at nagta-tar sa pagitan ng mga ito:

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 .

Walang kailangang i-install at walang naiiwang tumatakbong proseso. Ang restore ay kabaligtaran nito: tar xzf sa isang bagong walang-lamang volume na binaligtad ang mga mount.

May isang mahalagang limitasyon para sa mga database: ang pagta-tar ng running na Postgres data directory ay maaaring makakuha ng kalagayang nasa kalagitnaan ng pagsusulat, kaya maaaring hindi ito magsimula nang maayos. Alinman sa docker compose stop sa loob ng mga segundong kailangan ng tar, o mas mabuti, gumawa ng logical dump na consistent mula sa simula:

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

Dini-disable ng -T ang pseudo-terminal na awtomatikong ina-allocate ng Compose. Maaaring masira ang dump kapag ipinadaan ang output sa TTY. Ilagay ang isa sa mga ito sa cron at kopyahin ang resulta palabas ng VPS. Ang backup na nasa parehong disk ng data na pinoprotektahan nito ay kopya lamang, hindi backup. Binubuo ng Nextcloud guide ang isang kumpletong scheduled routine gamit mismo ang dalawang pattern na ito.

Mga failure mode at mga string na makikita mo

permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock, wala ka pa sa docker group, o kabilang ka na rito pero nauna ang session bago ito na-update. Ipinapakita ng id ang effective groups mo; inaayos ng newgrp docker ang kasalukuyang shell, habang inaayos silang lahat ng pag-log out at muling pag-log in.

Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?, ibang problema ito: hindi tumatakbo ang daemon mismo. Ipinapakita ng sudo systemctl status docker at sudo journalctl -u docker -n 50 kung bakit. Sa isang VPS, karaniwang sanhi ang punô na disk, kaya patakbuhin muna ang df -h /var/lib/docker.

Bind for 127.0.0.1:8080 failed: port is already allocated, may isa pang container na nag-publish na ng host port na iyon. Ipinapakita ng docker ps kung alin; karaniwang sanhi ang lumang container mula sa isang eksperimentong docker run ilang linggo na ang nakalipas. Kung malinis ang docker ps, non-Docker process ang gumagamit ng port: tinutukoy ito ng sudo ss -tlnp | grep 8080.

yaml: line 14: did not find expected key, may indentation error sa mismong line na tinukoy o kaagad bago nito. YAML ang Compose files: dalawang space ang indentation, spaces lamang ang gamitin, at fatal ang tab character kahit saan. Bine-validate ng docker compose config ang file nang hindi nagsisimula ng anuman, kaya magandang murang habit ang patakbuhin ito pagkatapos ng bawat edit.

Ang sorpresa sa ufw ay wala itong inilalabas na error, kaya mapanganib ito: matagumpay ang deploy, mukhang tama ang ufw status, pero nakikita pa rin ng port scan mula sa labas ang database mo. Basahin muli ang ports section sa itaas, suriin ang bawat ports: entry kung may nawawalang 127.0.0.1: prefix, at kumpirmahin mula sa ibang machine gamit ang curl http://your-vps-ip:8080; ang connection refused ang sagot na gusto mo.

Mula rito, ginagawang maraming app ng Traefik guide ang iisang stack sa likod ng isang HTTPS entry point, at ang mga sulit i-self-host sa 2026 ang shopping list na maaari mong patakbuhin dito. Kapag tumatakbo na ang ilan sa mga stack na iyon at bawat isa ay may sarili nitong login form, pinagsasama ng isang self-hosted SSO server gaya ng Authentik ang mga ito sa iisang account sa likod ng parehong proxy.

Ang game server gaya ng Minecraft server sa isang VPS ay magandang unang Compose project para pagpraktisan. Kung mas gusto mong matuto gamit ang app na binubuksan mo araw-araw, ang openGym, isang self-hosted workout tracker ay maliit na stack na naka-pin sa git tag sa halip na image tag. Kailangan nito ng TLS sa harap bago ka mag-register ng unang passkey. Karaniwang photos ang unang gustong ilipat ng mga tao palabas sa cloud ng ibang provider. Ang paghahambing ng PhotoPrism at Immich ay tumutulong magtakda ng minimum RAM at backup routine na kakailanganin mo bago ka mag-commit ng volume sa alinman sa mga ito. Kapag hindi na sapat ang dalawang serbisyo, ang pag-set up ng AFFiNE bilang Notion-style workspace ay gumagamit ng parehong mga pattern sa apat na container. Magandang pagsubok din ito kung naging nakasanayan mo na ang pinned tags, healthchecks, at named volumes sa itaas.

FAQ

Bakit ko nakukuha ang “permission denied while trying to connect to the Docker daemon socket”?

Wala ang user mo sa docker group, o naidagdag ito matapos magsimula ang kasalukuyang session; nalalapat lamang ang membership kapag nag-login. Patakbuhin ang sudo usermod -aG docker $USER, pagkatapos ay ang newgrp docker, o mag-log out at mag-log in muli, at kumpirmahin gamit ang id. Nagbibigay ang group ng access na katumbas ng root sa host, kaya magdagdag lamang ng mga user na pagkakalooban mo rin ng sudo.

Buburahin ba ng docker compose down ang data ko?

Hindi binubura ng plain na docker compose down ang data; inaalis nito ang containers at project network. Nananatili ang named volumes, at muling ikinakabit ng susunod na up -d ang mga ito. Ang docker compose down -v ang destructive form: binubura nito ang named volumes, kabilang ang database, nang walang confirmation at walang paraan para i-undo. Huwag kailanman patakbuhin ang -v sa stack na may aktuwal na data maliban kung mayroon kang verified backup.

Ano ang pagkakaiba ng docker-compose at docker compose?

Ang docker-compose (may hyphen) ay Compose v1, isang standalone Python binary na end of life na noong 2023 at hindi dapat i-install sa mga bagong server. Ang docker compose (may space) ay Compose v2, isang Go plugin para sa Docker CLI, na ini-install bilang docker-compose-plugin mula sa Docker apt repository. Halos ganap na compatible ang commands at YAML, kaya kapag sinasabi ng lumang tutorial na docker-compose up, i-type ang docker compose up.

Bakit naa-access ko ang Docker container mula sa internet kahit bina-block ng ufw ang port?

Dahil nagpa-publish ang Docker ng ports gamit ang DNAT rules sa PREROUTING chain ng iptables, at dumadaan ang mga packet na binago ang destination sa FORWARD path sa pamamagitan ng sariling chains ng Docker. Hindi na nila nadaraanan ang INPUT chain kung saan nalalapat ang rules ng ufw. Kaya walang epekto ang ufw deny 8080 sa published container port. Ayusin ito sa pinagmumulan: i-publish sa 127.0.0.1: at ilantad ang mga serbisyo sa pamamagitan ng reverse proxy.

Dapat ba akong gumamit ng named volume o bind mount?

Gumamit ng named volumes para sa data na container lamang ang gumagamit, lalo na sa mga database, dahil itinatakda ng Docker ang ownership na inaasahan ng image at karaniwang gumagana agad ang permissions. Gumamit ng bind mounts para sa mga file na hinahawakan mo rin mula sa host: mga config na ine-edit mo, media na ina-upload mo, at anumang path na gusto mong madaling makita. Kung hindi nagse-start ang isang container at lumalabas ang permission denied sa isang bind mount, unang suriin ang UID mismatch sa pagitan ng host at container.