SSD Nodes Learn RAM 8GB — $66/tahun
Panduan Matt ConnorOleh Matt Connor · Diperbarui 2026-08-01

Docker Compose Otomatis Mulai Saat Boot

Pelajari cara membuat layanan Docker Compose aktif kembali setelah reboot dengan restart policy, alasan on-failure tidak bertahan, dan kapan memakai systemd.

Jawaban singkat

Layanan Docker Compose dimulai saat boot jika dua kondisi terpenuhi secara bersamaan. Daemon Docker harus diaktifkan sebagai layanan sistem, dan setiap layanan dalam file harus memiliki kebijakan mulai ulang unless-stopped atau always. Tambahkan restart: unless-stopped ke setiap layanan, jalankan docker compose up -d sekali, lalu container akan dimulai kembali secara otomatis setelah reboot. Untuk kasus umum, tidak diperlukan hal lain.

Anda hanya memerlukan unit systemd jika urutan menjadi penting: misalnya, stack bergantung pada disk ter-mount, antarmuka VPN, atau network share yang belum siap saat daemon Docker dimulai. Kasus tersebut memang dapat terjadi, dan bagian kedua panduan ini membahasnya. Jika Anda masih mempelajari definisi layanan dan volume, mulai dengan dasar-dasar Docker Compose pada VPS, lalu kembali ke sini.

Atur kebijakan restart di compose.yaml

Kebijakan ini ditetapkan satu baris untuk setiap layanan. Tidak ada pengaturan global. Karena itu, layanan yang lupa dikonfigurasi akan tetap berhenti setelah reboot, sedangkan layanan lain dalam stack akan berjalan kembali.

services:
  app:
    image: nginx:1.27
    restart: unless-stopped
    ports:
      - "8080:80"
  db:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_PASSWORD: change-me
    volumes:
      - dbdata:/var/lib/postgresql/data

volumes:
  dbdata:

Terapkan perubahan, lalu baca kembali kebijakan dari container yang sedang berjalan:

docker compose up -d
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app)

Perintah tersebut menampilkan unless-stopped. Jika yang ditampilkan adalah no, berarti file sudah diedit, tetapi container belum pernah dibuat ulang.

Ini adalah kegagalan yang paling umum. Kebijakan restart disimpan pada container, bukan pada file YAML. Mengedit compose.yaml tidak mengubah container yang sudah ada. docker compose restart juga tidak membantu, karena perintah tersebut hanya menghentikan dan menjalankan kembali objek container yang sama tanpa mengubah konfigurasinya. Hanya docker compose up -d yang membandingkan file dengan container yang sedang berjalan, mendeteksi perubahan kebijakan, lalu membuat ulang container tersebut.

Untuk container yang belum ingin Anda buat ulang, ubah kebijakannya secara langsung:

docker update --restart unless-stopped my-container

Tetap edit file YAML. docker update mengubah container yang sedang berjalan, sedangkan docker compose up -d berikutnya akan membaca file dan mengembalikan nilai lama.

Fungsi sebenarnya setiap nilai restart

Docker menetapkan empat nilai. Perbedaannya hanya terlihat saat mesin melakukan boot ulang atau daemon dimulai ulang.

  • no adalah nilai default. Container tidak pernah dimulai ulang secara otomatis, dalam kondisi apa pun.
  • always memulai ulang container setiap kali container berhenti. Jika Anda menghentikannya secara manual, container akan berjalan kembali saat daemon Docker dimulai berikutnya. Hal ini sering mengejutkan: container yang sengaja Anda hentikan minggu lalu kembali berjalan setelah reboot.
  • unless-stopped berperilaku seperti always, tetapi container yang dihentikan secara manual tetap berhenti setelah daemon dimulai ulang. Gunakan nilai ini untuk layanan yang sesekali Anda hentikan untuk pemeliharaan.
  • on-failure memulai ulang container hanya jika container keluar dengan kode keluar non-zero. Anda dapat membatasi jumlah percobaan, seperti pada restart: on-failure:3.

Untuk stack yang harus selalu aktif selama server aktif, unless-stopped adalah nilai default yang tepat. Pilih always hanya jika Anda ingin container tidak mudah dibiarkan dalam keadaan berhenti.

Mengapa restart: on-failure tidak bertahan setelah reboot

Banyak orang memilih on-failure karena terlihat hati-hati, lalu mendapati semua container berhenti setelah reboot pertama. Penyebabnya terdapat dalam definisinya. on-failure hanya merespons satu hal: proses container keluar dengan kode kesalahan.

Reboot bukan kesalahan. Saat host dimatikan, systemd menghentikan docker.service, lalu daemon menghentikan setiap container secara sengaja. Container tidak gagal, sehingga tidak ada yang direspons oleh kebijakan tersebut. Saat sistem kembali aktif, daemon memeriksa container yang perlu dilanjutkan, dan container on-failure yang dihentikan secara normal tidak termasuk di dalamnya. Container tetap berada dalam status exited.

Anda dapat melihatnya secara langsung. Tetapkan restart: on-failure pada suatu service, jalankan docker compose up -d, lakukan reboot, lalu jalankan:

docker compose ps -a

Service tersebut tercantum dengan status Exited dan status seperti Exited (0) 2 minutes ago. Tidak ada yang rusak dan tidak ada yang dicatat sebagai kesalahan. Hal inilah yang membuat masalah tersebut sulit didiagnosis. Kebijakan itu berjalan tepat sesuai penjelasannya.

on-failure tetap berguna. Kebijakan ini sesuai untuk container yang menjalankan suatu pekerjaan dan mungkin mengalami crash, ketika Anda menginginkan jumlah percobaan ulang yang dibatasi tanpa restart loop. Namun, kebijakan ini bukan alat yang tepat untuk menjaga service yang berjalan lama tetap aktif setelah reboot.

Kebijakan restart hanya berfungsi jika layanan Docker dimulai saat boot

Kebijakan restart diterapkan oleh daemon Docker. Jika daemon tidak dimulai, tidak ada yang menerapkan kebijakan tersebut. Periksa:

systemctl is-enabled docker
systemctl is-enabled containerd

Keduanya harus menampilkan enabled. Paket dari repositori resmi Docker mengaktifkan keduanya saat instalasi, sehingga pada server baru pemeriksaan ini biasanya berhasil. Jika salah satunya menampilkan disabled, perbaiki:

sudo systemctl enable --now docker containerd

Ada hal yang perlu dipahami di sini. Ubuntu juga menyediakan docker.socket, yang memulai daemon sesuai permintaan saat pertama kali sesuatu berkomunikasi dengan Docker API. Orang melihat docker.socket aktif, menganggap daemon sudah tercakup, lalu menonaktifkan docker.service untuk menghemat memori. Saat boot, tidak ada yang memanggil API, sehingga socket tidak pernah diakses, daemon tidak pernah dimulai, dan tidak ada container yang berjalan sampai Anda mengetikkan perintah docker pertama. Aktivasi socket bukan pengganti untuk mengaktifkan docker.service.

Saat unit systemd menjadi pilihan yang lebih tepat

Kebijakan mulai ulang tidak memahami urutan terhadap bagian sistem lainnya. Daemon dimulai, lalu menjalankan container Anda segera setelah memungkinkan. Jika stack Anda melakukan bind mount direktori dari volume terpisah, share NFS (network file system), atau disk terenkripsi, container mungkin dimulai sebelum path tersebut tersedia. Docker akan membuat direktori kosong pada titik mount dan memulai container dengan direktori tersebut, sehingga database Anda berjalan tanpa data.

Tulis unit systemd jika salah satu kondisi berikut berlaku. Stack memerlukan mount, antarmuka VPN, atau unit lain yang harus siap lebih dahulu. Anda ingin systemctl stop myapp dan systemctl start myapp berfungsi seperti pada setiap layanan lain di server. Atau, Anda ingin stack dihentikan dengan benar saat shutdown, bukan dihentikan secara paksa bersama daemon. Jika unit systemd masih baru bagi Anda, menulis service dan timer systemd membahas format file tersebut secara lebih mendetail.

Letakkan stack di jalur tetap di luar direktori home. /srv/myapp adalah pilihan yang baik karena unit yang berjalan sebelum siapa pun login tidak perlu membaca /home.

Buat /etc/systemd/system/myapp.service:

[Unit]
Description=myapp docker compose stack
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target
RequiresMountsFor=/srv/myapp/data

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/srv/myapp
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0

[Install]
WantedBy=multi-user.target

Aktifkan dan jalankan:

sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
systemctl status myapp.service

Unit yang sehat menampilkan Active: active (exited). Status ini tampak salah saat pertama kali melihatnya. Status tersebut benar: Type=oneshot dengan RemainAfterExit=yes berarti unit telah menjalankan perintahnya, perintah tersebut selesai, dan systemd mempertahankan status unit sebagai aktif agar ExecStop berjalan saat shutdown.

Setiap baris memiliki fungsi. Requires=docker.service berarti unit gagal dengan cepat, bukan menjalankan docker compose terhadap socket yang tidak aktif. After= menetapkan urutan karena Requires= saja tidak cukup. RequiresMountsFor= membuat systemd memuat unit mount untuk jalur tersebut dan menunggunya. Inilah alasan utama menggunakan unit, bukan kebijakan restart. TimeoutStartSec=0 mencegah systemd menghentikan tugas start saat image besar masih di-pull.

Catatan tentang penggabungan kedua mekanisme. Dokumentasi Docker menyarankan agar kebijakan restart tidak digabungkan dengan pengelola proses host. Peringatan tersebut berlaku untuk pengelola proses yang mengawasi proses container itu sendiri dan merestartnya saat daemon juga sedang melakukan hal yang sama. Unit Type=oneshot tidak mengawasi apa pun. Oleh karena itu, mempertahankan restart: unless-stopped di file compose bersama unit ini tidak masalah dan memang itulah yang diperlukan. systemd menangani pengurutan saat boot, sedangkan daemon menangani container yang mengalami crash pada pukul tiga pagi.

Verifikasi dengan reboot nyata

Tidak ada pengganti untuk pengujian nyata. systemctl restart docker tidak menguji urutan mount, sedangkan docker compose down yang diikuti docker compose up -d sama sekali tidak menguji proses boot.

sudo reboot

Tunggu, sambungkan kembali, lalu periksa dengan urutan berikut:

uptime
systemctl is-active docker
docker compose ps

uptime mengonfirmasi bahwa Anda melihat mesin yang benar-benar telah reboot. docker compose ps, jika dijalankan dari direktori stack, seharusnya mencantumkan setiap layanan sebagai running dengan uptime yang mendekati uptime mesin. Layanan yang menampilkan Exited adalah layanan yang perlu diperiksa.

Jika ada yang tidak berhasil aktif, log daemon mencakup periode boot:

journalctl -u docker.service -b --no-pager | tail -50

Untuk stack yang dikelola oleh unit, journalctl -u myapp.service -b --no-pager menampilkan output docker compose yang persis dari proses boot, termasuk kegagalan pull image atau file .env yang hilang.

Hal-Hal yang Diam-Diam Mengganggu Start Otomatis

Container yang dibuat dengan docker compose run tidak pernah mendapatkan kebijakan restart dari file tersebut. Compose memperlakukannya sebagai container sekali jalan. Jika suatu service tampak mengabaikan kebijakannya, periksa apakah service tersebut dijalankan dengan run, bukan up.

Path relatif dalam volume atau entri env_file ditentukan berdasarkan direktori file compose. Ini berfungsi dari shell Anda dan dari unit yang menetapkan WorkingDirectory. Ini gagal dari unit yang tidak menetapkannya, karena direktori kerjanya adalah /.

Rootless Docker adalah kasus terpisah. Daemonnya berjalan sebagai user service, dan user service berhenti ketika sesi terakhir untuk pengguna tersebut berakhir. Aktifkan service tersebut untuk pengguna itu dan izinkan service tersebut tetap berjalan tanpa ada pengguna yang login:

systemctl --user enable docker
sudo loginctl enable-linger $USER

Tanpa enable-linger, daemon rootless akan mati saat Anda logout dan container ikut berhenti. Kondisi ini tampak persis seperti kebijakan restart yang rusak.

Satu hal terakhir. Pembaruan keamanan otomatis dapat me-reboot server pada jam tertentu. Hal ini hanya menguntungkan jika stack Anda dapat berjalan kembali secara otomatis. Konfigurasi ini pada mesin baru termasuk pekerjaan jam pertama lainnya di sepuluh menit pertama pada VPS baru.

FAQ

Apa perbedaan antara restart: always dan restart: unless-stopped?

Keduanya memulai ulang container saat container berhenti sendiri. Perbedaannya terlihat setelah Anda menghentikan container secara manual. Dengan always, container dimulai lagi saat daemon Docker dimulai berikutnya. Dengan demikian, reboot membatalkan penghentian manual Anda. Dengan unless-stopped, daemon mengingat bahwa container sengaja dihentikan dan membiarkannya tetap berhenti. Gunakan unless-stopped, kecuali Anda memang ingin container tetap berhenti.

Saya menambahkan restart: unless-stopped, tetapi container tetap tidak dimulai setelah reboot. Mengapa?

Kebijakan tersebut tersimpan pada container, bukan di dalam file. Container yang sudah ada tidak diperbarui hanya dengan mengedit YAML. Jalankan docker compose up -d agar Compose membuat ulang container, lalu konfirmasikan dengan docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app). Jika perintah tersebut menampilkan no, container dibuat sebelum perubahan Anda. Penyebab umum lainnya adalah docker.service belum diaktifkan. Anda dapat memeriksanya dengan systemctl is-enabled docker.

Apakah saya memerlukan unit systemd jika sudah menggunakan kebijakan restart?

Biasanya tidak. Kebijakan restart cukup untuk stack yang hanya memerlukan jaringan, dan sebagian besar stack termasuk kategori ini. Tambahkan unit jika container bergantung pada sesuatu yang belum siap saat daemon Docker dimulai, seperti disk eksternal, volume terenkripsi, share NFS, atau antarmuka VPN. Unit tersebut menyediakan pengaturan urutan melalui After= dan RequiresMountsFor=, yang tidak dapat dinyatakan oleh kebijakan restart.

Bagaimana cara menghentikan stack secara permanen agar tidak dimulai lagi pada reboot berikutnya?

Dengan unless-stopped, docker compose stop sudah cukup karena container yang dihentikan secara manual tidak dimulai kembali saat daemon dimulai ulang. Dengan always, penghentian saja tidak cukup dan container akan kembali setelah reboot. Jalankan docker compose down untuk menghapus container, atau ubah kebijakan terlebih dahulu dengan docker update --restart no my-container. Jika unit systemd mengelola stack tersebut, jalankan sudo systemctl disable myapp.service juga. Jika tidak, unit akan memulainya lagi.