{"id":13041,"date":"2026-09-16T07:35:00","date_gmt":"2026-09-16T05:35:00","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/?p=13041"},"modified":"2026-09-09T13:22:36","modified_gmt":"2026-09-09T11:22:36","slug":"tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi","status":"publish","type":"post","link":"https:\/\/www.lukaswojcik.com\/blog\/en\/raspberry-pi\/tutorials-en-raspberry-pi\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\/","title":{"rendered":"Tutorial: Hardening a Docker Compose File for Unattended Operation on a Raspberry Pi"},"content":{"rendered":"<p>A Compose file that starts a stack is written in ten minutes. A Compose file that keeps the stack running for a year takes about six more lines per service, and each of those lines exists because of a specific way things fall over.<\/p>\n<p>What follows goes through them in the order they usually become necessary &#8211; which is roughly the order in which a machine left alone runs into them.<\/p>\n<figure class=\"lw-diagram\">\n<img src=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/diagrams\/compose-haerten-en.png\" width=\"1120\" height=\"580\" decoding=\"async\" loading=\"lazy\"\n     alt=\"Five failures over ninety days on a rail, each with the single Compose line that would have prevented it, beside the finished hardened service definition\"><figcaption>None of these five needs a rare circumstance. They are what ninety days of running unattended produces on its own.<\/figcaption><\/figure>\n<h2>The Line That Decides Whether It Comes Back<\/h2>\n<p>A container that stops stays stopped. After a power cut, after a crash, after a daemon restart &#8211; unless a restart policy says otherwise.<\/p>\n<pre class=\"wp-block-kevinbatdorf-code-block-pro\"><code>services:\n  app:\n    restart: unless-stopped<\/code><\/pre>\n<p>Three values are worth telling apart. <code>no<\/code> is the default and means nothing comes back. <code>always<\/code> restarts the container in every case, including after it was stopped by hand and the machine then rebooted &#8211; which is how a container deliberately taken out of service reappears at three in the morning. <code>unless-stopped<\/code> does the same thing except in that one case, and is the right default for a machine nobody is watching.<\/p>\n<p>Two details behind it. Docker backs off between restart attempts, doubling from 100 milliseconds up to a minute, so a container in a crash loop does not saturate the machine &#8211; but it also does not stop trying, and nothing draws attention to it. And the restart policy only applies to a container that exits. A process that hangs while its container stays up is exactly the case the next section covers.<\/p>\n<h2>A Healthcheck That Tests Something<\/h2>\n<p>A hung application is worse than a crashed one, because everything around it believes it is working. The container is running, the port is open, and the requests go nowhere.<\/p>\n<pre class=\"wp-block-kevinbatdorf-code-block-pro\"><code>    healthcheck:\n      test: [\"CMD\", \"curl\", \"-fsS\", \"http:\/\/localhost:8080\/health\"]\n      interval: 30s\n      timeout: 5s\n      retries: 3\n      start_period: 60s<\/code><\/pre>\n<p>The last line is the one most often missing and the one that causes the most confusion. During <code>start_period<\/code>, a failing check does not count towards <code>retries<\/code>. Without it, a service that needs forty seconds to come up is declared unhealthy on the way there and restarted, forever.<\/p>\n<p>What the check tests matters more than that it exists. A request to <code>\/<\/code> proves the web server answers, which it will do while the database connection behind it has been gone for an hour. An endpoint that touches the parts that can fail is worth writing for exactly this purpose &#8211; and it should return quickly, because it runs every thirty seconds for the life of the machine.<\/p>\n<p>A healthcheck also makes an ordering statement possible, which <code>depends_on<\/code> alone does not. Without the condition, Compose starts the dependency and moves on immediately; with it, it waits.<\/p>\n<pre class=\"wp-block-kevinbatdorf-code-block-pro\"><code>  app:\n    depends_on:\n      db:\n        condition: service_healthy<\/code><\/pre>\n<p>One consequence for slim images: <code>curl<\/code> is often not installed, and a healthcheck that cannot run counts as a failure. Either the check uses something the image has &#8211; <code>wget -q --spider<\/code>, or a command the application itself brings &#8211; or the image gets a package it would otherwise not need.<\/p>\n<h2>Logs That Do Not Fill the Card<\/h2>\n<p>The default logging driver writes to a JSON file that grows without limit. On a server with a large disk that is untidy; on a Pi with a memory card it is a failure mode, because a full file system stops the whole machine rather than the noisy container.<\/p>\n<pre class=\"wp-block-kevinbatdorf-code-block-pro\"><code>    logging:\n      driver: json-file\n      options:\n        max-size: \"10m\"\n        max-file: \"3\"<\/code><\/pre>\n<p>Thirty megabytes per service is generous for anything that is not being actively debugged. The setting belongs on every service and not only on the chatty one, because the chatty one is usually the service that starts logging a stack trace once a second at some point.<\/p>\n<p>The same limit can be set once for the whole machine in <code>\/etc\/docker\/daemon.json<\/code>, which is the better place for it &#8211; though it only applies to containers created afterwards, so an existing stack has to be recreated for it to take effect.<\/p>\n<pre class=\"wp-block-kevinbatdorf-code-block-pro\"><code>{\n  \"log-driver\": \"json-file\",\n  \"log-opts\": { \"max-size\": \"10m\", \"max-file\": \"3\" }\n}<\/code><\/pre>\n<p>Where the writes themselves are the problem rather than the space, a memory card benefits from moving the logs out of the file system entirely &#8211; <code>driver: journald<\/code> hands them to systemd, which can be told to keep them in RAM.<\/p>\n<h2>Memory Limits, and the Pi-Specific Trap<\/h2>\n<p>A machine with four gigabytes and no swap has no room to absorb a leak. The process that gets killed when memory runs out is chosen by the kernel, and it is regularly not the one responsible.<\/p>\n<pre class=\"wp-block-kevinbatdorf-code-block-pro\"><code>    deploy:\n      resources:\n        limits:\n          memory: 512M\n          cpus: \"1.5\"<\/code><\/pre>\n<p>A limit turns a machine-wide failure into a container-wide one: the offending container is killed and restarted by its restart policy, and everything else keeps running. That is a much better outcome than the alternative, and it is worth setting even when the number is a guess &#8211; a generous limit still bounds the damage.<\/p>\n<p>And here is the part specific to the Raspberry Pi. On Raspberry Pi OS the memory cgroup controller is disabled by default, and a memory limit in the Compose file is then accepted without complaint and has no effect. A warning appears once at daemon start and never again.<\/p>\n<pre class=\"wp-block-kevinbatdorf-code-block-pro\"><code># append to the single line in \/boot\/firmware\/cmdline.txt, then reboot\ncgroup_enable=memory cgroup_memory=1\n\n# afterwards, this line has to be gone:\ndocker info 2&gt;&amp;1 | grep -i \"no memory limit support\"<\/code><\/pre>\n<p>The file contains exactly one line and a line break added by an editor makes the machine unbootable, so the addition goes at the end of the existing line, separated by a space.<\/p>\n<h2>Pinning the Image So a Restart Is Not an Upgrade<\/h2>\n<p>A tag is a moving pointer. <code>postgres:16<\/code> means something different this month than last, and the moment it changes is not chosen by anybody &#8211; it is whenever the container happens to be recreated.<\/p>\n<pre class=\"wp-block-kevinbatdorf-code-block-pro\"><code>    image: postgres:16.4@sha256:f8cd4e0b9b0d15b6bbbcbbdf8c5f8c8b6c6b3e9b7cbb4f2d1e3a5c7d9f0b2a46<\/code><\/pre>\n<p>A digest is immutable, which turns &#8220;recreate the container&#8221; into an operation with a known result. The tag stays in front of it as documentation &#8211; the digest alone is unreadable, and a file full of hashes tells nobody what is running.<\/p>\n<p>The digest for an image already in use is one command away.<\/p>\n<pre class=\"wp-block-kevinbatdorf-code-block-pro\"><code>docker image inspect postgres:16.4 --format '{{index .RepoDigests 0}}'<\/code><\/pre>\n<p>One warning about the combination with automatic updates. A tool that pulls new images on a schedule and a pinned digest are opposites, and running both means the pin quietly wins while the update tool reports success. Either the versions move on a schedule, or they move when somebody decides &#8211; both are defensible, and having one of them by accident is not.<\/p>\n<p>Two more lines belong in the same block and cost nothing. A container that never writes to its own file system can say so, and one that does not need to become root can be told it never will.<\/p>\n<pre class=\"wp-block-kevinbatdorf-code-block-pro\"><code>    read_only: true\n    tmpfs:\n      - \/tmp\n    security_opt:\n      - no-new-privileges:true\n    user: \"1000:1000\"<\/code><\/pre>\n<h2>Checking the File Before It Runs<\/h2>\n<p>Compose resolves variables, merges override files and applies defaults, and the result is often not what the file appears to say. One command prints what will actually be started.<\/p>\n<pre class=\"wp-block-kevinbatdorf-code-block-pro\"><code>docker compose config<\/code><\/pre>\n<p>Three things are worth looking for in that output. An empty value where a variable should be &#8211; an unset variable becomes an empty string without an error, and a missing password often reads as no password. An image without a digest. And a service without <code>restart<\/code>, without <code>healthcheck<\/code> or without <code>logging<\/code>, which the eye skips over easily in a file with eight services.<\/p>\n<p>The last one can be checked mechanically, which is worth doing because it is exactly the kind of omission that happens when a service is added quickly.<\/p>\n<pre class=\"wp-block-kevinbatdorf-code-block-pro\"><code>docker compose config --format json | python3 -c '\nimport json, sys\nstack = json.load(sys.stdin)\nfor name, svc in stack[\"services\"].items():\n    fehlt = [f for f in (\"restart\", \"healthcheck\", \"logging\") if f not in svc]\n    if \"@sha256:\" not in svc.get(\"image\", \"\"):\n        fehlt.append(\"digest\")\n    print(f\"{name:20s} {\\\", \\\".join(fehlt) if fehlt else \\\"ok\\\"}\")\n'<\/code><\/pre>\n<p>After the first run, one last check belongs to the routine and needs no tooling: reboot the machine on purpose and see what comes back. A stack that has never survived a restart has not been tested, and the moment to find that out is not the morning after a power cut.<\/p>\n<div class=\"lw-quellen\">\n<h2>Sources<\/h2>\n<ul>\n<li><a href=\"https:\/\/docs.docker.com\/\" target=\"_blank\" rel=\"noopener noreferrer\">Docker documentation<\/a><\/li>\n<li><a href=\"https:\/\/www.raspberrypi.com\/documentation\/computers\/raspberry-pi.html\" target=\"_blank\" rel=\"noopener noreferrer\">Raspberry Pi documentation<\/a><\/li>\n<\/ul>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>A stack that runs is not a stack that keeps running. Six lines per service decide whether a power cut, a hung process, a log file or an unpinned tag takes the machine down &#8211; and on a Pi one of them is silently ignored until a line is added to the boot configuration.<\/p>\n","protected":false},"author":1,"featured_media":13231,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[92630],"tags":[91144,91145,91181,91237,91219],"class_list":["post-13041","post","type-post","status-publish","format-standard","hentry","category-tutorials-en-raspberry-pi","tag-devops","tag-docker","tag-raspberry-pi","tag-self-hosting","tag-tutorial"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Tutorial: Hardening a Docker Compose File for Unattended Operation on a Raspberry Pi - Lukas Wojcik - Blog<\/title>\n<meta name=\"description\" content=\"Restart policy, healthcheck, log rotation, memory limit and a pinned digest: the six lines that keep a Compose stack running unattended on a Raspberry Pi.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/raspberry-pi\/tutorials-en-raspberry-pi\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Tutorial: Hardening a Docker Compose File for Unattended Operation on a Raspberry Pi - Lukas Wojcik - Blog\" \/>\n<meta property=\"og:description\" content=\"Restart policy, healthcheck, log rotation, memory limit and a pinned digest: the six lines that keep a Compose stack running unattended on a Raspberry Pi.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/en\/raspberry-pi\/tutorials-en-raspberry-pi\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-16T05:35:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/hero-13041-tutorial-hardening-a-docker-compose-file.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"luky\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"luky\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/raspberry-pi\\\/tutorials-en-raspberry-pi\\\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/raspberry-pi\\\/tutorials-en-raspberry-pi\\\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\\\/\"},\"author\":{\"name\":\"luky\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"headline\":\"Tutorial: Hardening a Docker Compose File for Unattended Operation on a Raspberry Pi\",\"datePublished\":\"2026-09-16T05:35:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/raspberry-pi\\\/tutorials-en-raspberry-pi\\\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\\\/\"},\"wordCount\":1242,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/raspberry-pi\\\/tutorials-en-raspberry-pi\\\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/hero-13041-tutorial-hardening-a-docker-compose-file.png\",\"keywords\":[\"DevOps\",\"Docker\",\"Raspberry Pi\",\"Self-Hosting\",\"Tutorial\"],\"articleSection\":[\"Tutorials\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/raspberry-pi\\\/tutorials-en-raspberry-pi\\\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/raspberry-pi\\\/tutorials-en-raspberry-pi\\\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/raspberry-pi\\\/tutorials-en-raspberry-pi\\\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\\\/\",\"name\":\"Tutorial: Hardening a Docker Compose File for Unattended Operation on a Raspberry Pi - Lukas Wojcik - Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/raspberry-pi\\\/tutorials-en-raspberry-pi\\\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/raspberry-pi\\\/tutorials-en-raspberry-pi\\\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/hero-13041-tutorial-hardening-a-docker-compose-file.png\",\"datePublished\":\"2026-09-16T05:35:00+00:00\",\"description\":\"Restart policy, healthcheck, log rotation, memory limit and a pinned digest: the six lines that keep a Compose stack running unattended on a Raspberry Pi.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/raspberry-pi\\\/tutorials-en-raspberry-pi\\\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/raspberry-pi\\\/tutorials-en-raspberry-pi\\\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/raspberry-pi\\\/tutorials-en-raspberry-pi\\\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/hero-13041-tutorial-hardening-a-docker-compose-file.png\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/hero-13041-tutorial-hardening-a-docker-compose-file.png\",\"width\":1200,\"height\":630,\"caption\":\"Tutorial: Hardening a Docker Compose File for Unattended Operation on a Raspberry Pi\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/raspberry-pi\\\/tutorials-en-raspberry-pi\\\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Tutorial: Hardening a Docker Compose File for Unattended Operation on a Raspberry Pi\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"luky\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"luky\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"],\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/author\\\/luky\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Tutorial: Hardening a Docker Compose File for Unattended Operation on a Raspberry Pi - Lukas Wojcik - Blog","description":"Restart policy, healthcheck, log rotation, memory limit and a pinned digest: the six lines that keep a Compose stack running unattended on a Raspberry Pi.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/en\/raspberry-pi\/tutorials-en-raspberry-pi\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\/","og_locale":"en_US","og_type":"article","og_title":"Tutorial: Hardening a Docker Compose File for Unattended Operation on a Raspberry Pi - Lukas Wojcik - Blog","og_description":"Restart policy, healthcheck, log rotation, memory limit and a pinned digest: the six lines that keep a Compose stack running unattended on a Raspberry Pi.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/en\/raspberry-pi\/tutorials-en-raspberry-pi\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\/","og_site_name":"Lukas Wojcik - Blog","article_published_time":"2026-09-16T05:35:00+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/hero-13041-tutorial-hardening-a-docker-compose-file.png","type":"image\/png"}],"author":"luky","twitter_card":"summary_large_image","twitter_misc":{"Written by":"luky","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/raspberry-pi\/tutorials-en-raspberry-pi\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\/#article","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/raspberry-pi\/tutorials-en-raspberry-pi\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\/"},"author":{"name":"luky","@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"headline":"Tutorial: Hardening a Docker Compose File for Unattended Operation on a Raspberry Pi","datePublished":"2026-09-16T05:35:00+00:00","mainEntityOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/raspberry-pi\/tutorials-en-raspberry-pi\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\/"},"wordCount":1242,"commentCount":0,"publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/raspberry-pi\/tutorials-en-raspberry-pi\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/hero-13041-tutorial-hardening-a-docker-compose-file.png","keywords":["DevOps","Docker","Raspberry Pi","Self-Hosting","Tutorial"],"articleSection":["Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/raspberry-pi\/tutorials-en-raspberry-pi\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/raspberry-pi\/tutorials-en-raspberry-pi\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\/","url":"https:\/\/www.lukaswojcik.com\/blog\/en\/raspberry-pi\/tutorials-en-raspberry-pi\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\/","name":"Tutorial: Hardening a Docker Compose File for Unattended Operation on a Raspberry Pi - Lukas Wojcik - Blog","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/raspberry-pi\/tutorials-en-raspberry-pi\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\/#primaryimage"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/raspberry-pi\/tutorials-en-raspberry-pi\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/hero-13041-tutorial-hardening-a-docker-compose-file.png","datePublished":"2026-09-16T05:35:00+00:00","description":"Restart policy, healthcheck, log rotation, memory limit and a pinned digest: the six lines that keep a Compose stack running unattended on a Raspberry Pi.","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/raspberry-pi\/tutorials-en-raspberry-pi\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/raspberry-pi\/tutorials-en-raspberry-pi\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/raspberry-pi\/tutorials-en-raspberry-pi\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\/#primaryimage","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/hero-13041-tutorial-hardening-a-docker-compose-file.png","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/hero-13041-tutorial-hardening-a-docker-compose-file.png","width":1200,"height":630,"caption":"Tutorial: Hardening a Docker Compose File for Unattended Operation on a Raspberry Pi"},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/raspberry-pi\/tutorials-en-raspberry-pi\/tutorial-hardening-a-docker-compose-file-for-unattended-operation-on-a-raspberry-pi\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Tutorial: Hardening a Docker Compose File for Unattended Operation on a Raspberry Pi"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"luky","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"luky"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"],"url":"https:\/\/www.lukaswojcik.com\/blog\/author\/luky\/"}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/13041","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=13041"}],"version-history":[{"count":1,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/13041\/revisions"}],"predecessor-version":[{"id":17275,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/13041\/revisions\/17275"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media\/13231"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=13041"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/categories?post=13041"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=13041"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}