Docker Compose Otomatis Berjalan Saat Boot
Pelajari cara membuat service Docker Compose aktif setelah reboot dengan restart policy, mengapa on-failure tidak cukup, dan kapan perlu unit systemd.
Jawaban singkat
Service Docker Compose akan berjalan saat boot jika dua kondisi terpenuhi secara bersamaan. Docker daemon harus diaktifkan sebagai system service, dan setiap service dalam file harus memiliki restart policy unless-stopped atau always. Tambahkan restart: unless-stopped ke setiap service, jalankan docker compose up -d satu kali, dan container akan berjalan kembali secara otomatis setelah reboot. Untuk kasus umum, tidak diperlukan hal lain.
Anda hanya memerlukan unit systemd jika urutan startup penting: misalnya stack bergantung pada disk yang di-mount, interface VPN, atau network share yang belum siap saat Docker daemon berjalan. Kasus ini memang dapat terjadi, dan bagian kedua panduan ini membahasnya. Jika Anda masih mempelajari definisi service dan volume, mulai dengan dasar-dasar Docker Compose pada VPS, lalu lanjutkan kembali ke panduan ini.
Atur kebijakan restart di compose.yaml
Kebijakan ini ditetapkan satu baris untuk setiap service. Tidak ada opsi global. Jadi, service yang terlewat akan tetap berhenti setelah reboot, sementara service lain dalam stack 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 menampilkan no, file sudah diedit, tetapi container belum pernah dibuat ulang.
Ini adalah kegagalan yang paling umum. Kebijakan restart disimpan pada container, bukan dalam 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 sekarang, ubah kebijakannya secara langsung:
docker update --restart unless-stopped my-containerTetap edit file YAML. docker update mengubah container yang sedang berjalan, sedangkan docker compose up -d berikutnya akan membaca file dan mengembalikan nilai lama.
Apa yang sebenarnya dilakukan setiap nilai restart
Docker menetapkan empat nilai. Perbedaannya hanya terlihat saat mesin melakukan reboot atau daemon dimulai ulang.
noadalah nilai default. Container tidak pernah dimulai ulang secara otomatis dalam kondisi apa pun.alwaysmemulai ulang container setiap kali container berhenti. Jika Anda menghentikannya secara manual, container tetap akan berjalan kembali saat daemon Docker dimulai berikutnya. Hal ini sering mengejutkan: container yang sengaja Anda hentikan minggu lalu berjalan kembali setelah reboot.unless-stoppedberperilaku sepertialways, tetapi container yang dihentikan secara manual tetap berhenti setelah daemon dimulai ulang. Gunakan nilai ini untuk service yang sesekali dihentikan untuk pemeliharaan.on-failurememulai ulang container hanya jika container keluar dengan kode exit non-zero. Anda dapat membatasi jumlah percobaan, seperti padarestart: on-failure:3.
Untuk stack yang harus selalu aktif selama server aktif, unless-stopped adalah default yang tepat. Pilih always hanya jika Anda ingin container yang tidak mudah dibiarkan berhenti.
Mengapa restart: on-failure tidak bertahan setelah reboot
Banyak orang memilih on-failure karena terdengar hati-hati, lalu mendapati semua container berhenti setelah reboot pertama. Penyebabnya terdapat dalam definisinya. on-failure hanya bereaksi terhadap satu hal: proses container keluar dengan kode error.
Reboot bukan error. Saat host dimatikan, systemd menghentikan docker.service, lalu daemon menghentikan setiap container secara sengaja. Container tidak gagal, sehingga policy tersebut tidak memiliki kondisi untuk ditangani. Saat sistem kembali aktif, daemon memeriksa container yang perlu dilanjutkan, dan container on-failure yang dihentikan secara normal tidak termasuk di dalamnya. Container tersebut tetap berada dalam status exited.
Anda dapat melihatnya secara langsung. Tetapkan restart: on-failure pada sebuah service, jalankan docker compose up -d, reboot, lalu jalankan:
docker compose ps -aService tersebut tercantum dengan state Exited dan status seperti Exited (0) 2 minutes ago. Tidak ada yang rusak dan tidak ada yang dicatat sebagai error. Inilah yang membuat masalah ini sulit didiagnosis. Policy tersebut berjalan persis sesuai dengan fungsinya.
on-failure tetap berguna. Policy ini cocok untuk container yang menjalankan job dan mungkin mengalami crash, ketika Anda menginginkan jumlah percobaan ulang yang dibatasi tanpa restart loop. Namun, policy ini bukan pilihan yang tepat untuk menjaga service yang berjalan lama tetap aktif setelah reboot.
Kebijakan restart hanya berfungsi jika service Docker berjalan saat boot
Kebijakan restart diberlakukan oleh daemon Docker. Jika daemon tidak berjalan, tidak ada yang memberlakukannya. Periksa:
systemctl is-enabled docker
systemctl is-enabled containerdKeduanya 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 containerdAda hal penting yang perlu dipahami. Ubuntu juga menyediakan docker.socket, yang menjalankan daemon sesuai permintaan saat pertama kali ada sesuatu yang berkomunikasi dengan Docker API. Banyak orang melihat docker.socket aktif, mengira daemon sudah tercakup, lalu menonaktifkan docker.service untuk menghemat memori. Saat boot, tidak ada yang memanggil API. Akibatnya, socket tidak pernah diakses, daemon tidak pernah berjalan, dan tidak ada container yang dijalankan sampai Anda mengetik perintah docker pertama. Aktivasi socket bukan pengganti untuk mengaktifkan docker.service.
Kapan unit systemd menjadi pilihan yang lebih tepat
Kebijakan restart tidak memiliki konsep pengurutan terhadap sistem lainnya. Daemon dijalankan, lalu segera menjalankan container setelah memungkinkan. Jika stack Anda melakukan bind mount direktori dari volume terpisah, share NFS (network file system), atau disk terenkripsi, container dapat dijalankan sebelum path tersebut tersedia. Docker akan membuat direktori kosong pada titik mount dan menjalankan container dengan direktori tersebut, sehingga database Anda berjalan tanpa data.
Tulis unit systemd jika salah satu kondisi berikut berlaku. Stack memerlukan mount, interface VPN, atau unit lain yang harus siap terlebih dahulu. Anda ingin systemctl stop myapp dan systemctl start myapp berfungsi seperti pada service 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 menjelaskan format file tersebut secara lebih mendalam.
Menulis unit systemd
Letakkan stack di path 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.targetAktifkan dan jalankan:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
systemctl status myapp.serviceUnit yang sehat menampilkan Active: active (exited). Status ini tampak salah saat pertama kali Anda 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 dijalankan saat shutdown.
Setiap baris memiliki fungsi. Requires=docker.service berarti unit gagal dengan cepat, bukan menjalankan docker compose pada socket yang sudah tidak aktif. After= menetapkan urutan, karena Requires= saja tidak melakukannya. RequiresMountsFor= membuat systemd memuat unit mount untuk path tersebut dan menunggunya. Inilah alasan utama menggunakan unit, bukan kebijakan restart. TimeoutStartSec=0 mencegah systemd menghentikan job start saat image berukuran besar masih di-pull.
Catatan tentang penggabungan kedua mekanisme. Dokumentasi Docker menyarankan agar kebijakan restart tidak digabungkan dengan process manager pada host. Peringatan tersebut berlaku untuk process manager yang mengawasi proses container secara langsung dan me-restart-nya saat daemon juga mencoba melakukan hal yang sama. Unit Type=oneshot tidak mengawasi proses apa pun. Karena itu, mempertahankan restart: unless-stopped di file compose bersama unit ini tidak masalah, dan memang itulah yang diperlukan. systemd menangani urutan saat boot, sedangkan daemon menangani container yang crash pada pukul tiga pagi.
Unit ini terlihat berbeda jika proses yang harus tetap berjalan adalah proses biasa yang berjalan lama, bukan stack. Dalam kasus tersebut, tidak ada daemon yang mendasarinya, sehingga Restart= milik systemd harus melakukan pengawasan. menjalankan dsh tanpa antarmuka di belakang systemd adalah contoh lengkap untuk pola tersebut, termasuk user khusus dan journal.
Verifikasi dengan reboot nyata
Tidak ada pengganti untuk pengujian nyata. systemctl restart docker tidak menguji urutan pemasangan, sedangkan docker compose down yang diikuti docker compose up -d sama sekali tidak menguji proses boot.
sudo rebootTunggu, sambungkan kembali, lalu lakukan pemeriksaan dalam urutan berikut:
uptime
systemctl is-active docker
docker compose psuptime memastikan bahwa Anda sedang memeriksa mesin yang benar-benar telah reboot. docker compose ps, jika dijalankan dari direktori stack, seharusnya menampilkan setiap service dengan status running dan uptime yang mendekati uptime mesin. Service yang menampilkan Exited adalah service yang perlu diperiksa.
Jika ada sesuatu yang tidak berhasil berjalan, log daemon mencakup periode boot:
journalctl -u docker.service -b --no-pager | tail -50Untuk stack yang dikelola oleh unit, journalctl -u myapp.service -b --no-pager menampilkan output docker compose yang tepat sejak boot, termasuk kegagalan mengambil image atau file .env yang tidak ada. Reboot yang Anda jadwalkan adalah reboot yang Anda pantau, jadi biarkan unit memberi tahu Anda tentang reboot yang tidak Anda pantau: baris OnFailure= yang diarahkan ke server ntfy yang di-host sendiri mengubah stack yang gagal kembali berjalan menjadi notifikasi push, bukan masalah yang baru Anda temukan beberapa hari kemudian.
Hal-hal yang dapat diam-diam menghambat auto-start
Container yang dibuat dengan docker compose run tidak pernah mendapatkan restart policy dari file tersebut. Compose memperlakukannya sebagai container sekali jalan. Jika sebuah service tampaknya mengabaikan policy-nya, periksa apakah service tersebut dijalankan dengan run, bukan up.
Relative path dalam volume atau entri env_file di-resolve berdasarkan direktori file compose. Ini berfungsi dari shell Anda dan dari unit yang menetapkan WorkingDirectory. Namun, ini gagal dari unit yang tidak menetapkannya karena working directory-nya adalah /.
Rootless Docker adalah kasus yang berbeda. Daemon berjalan sebagai user service, dan user service berhenti ketika sesi terakhir untuk user tersebut berakhir. Aktifkan service tersebut untuk user itu dan izinkan daemon tetap berjalan saat tidak ada user yang login:
systemctl --user enable docker
sudo loginctl enable-linger $USERTanpa enable-linger, rootless daemon akan berhenti saat Anda logout dan container ikut berhenti. Kondisi ini tampak persis seperti restart policy yang rusak.
Satu hal terakhir. Automatic security updates dapat me-reboot server pada jam yang ditentukan. Ini hanya bermanfaat jika stack Anda dapat berjalan kembali secara otomatis. Konfigurasi ini pada mesin baru termasuk bagian dari pekerjaan satu jam pertama, bersama sepuluh menit pertama pada VPS baru.
FAQ
Apa perbedaan antara restart: always dan restart: unless-stopped?
Keduanya me-restart container ketika container berhenti sendiri. Perbedaannya terlihat setelah Anda menghentikan container secara manual. Dengan always, container akan start lagi saat Docker daemon start berikutnya, sehingga 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 start 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 dalam kategori ini. Tambahkan unit jika container bergantung pada sesuatu yang belum siap saat Docker daemon start, seperti disk eksternal, volume terenkripsi, share NFS, atau interface VPN. Unit tersebut mengatur urutan melalui After= dan RequiresMountsFor=, yang tidak dapat dinyatakan oleh kebijakan restart.
Bagaimana cara menghentikan stack secara permanen agar tidak start lagi setelah reboot?
Dengan unless-stopped, docker compose stop sudah cukup, karena container yang dihentikan secara manual tidak akan dijalankan kembali saat daemon start ulang. Dengan always, penghentian saja tidak cukup dan container akan kembali start setelah reboot. Jalankan docker compose down untuk menghapus container, atau ubah kebijakannya 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 menjalankannya kembali.