banner docker mistakes -- Egor Komarov/Unsplash/ThomasDyan/RPTips

7 Docker Mistakes That Will Eventually Break Your Raspberry Pi

If you click our links and make a purchase, we may earn an affiliate commission. Learn more

I’ve been using Docker more and more on my Raspberry Pi, especially to host services that don’t have a package in the default repo. It’s great for quick tests, but over time, I start running into issues. That’s why today I’ll share tips on how to make your containers last.

Docker containers can be kept running indefinitely if configured well. For stability, keep containers isolated, each with separate config files, folders, and ports. For easy maintenance, enable automatic restarts, updates, and backups.

You’re probably already familiar with these principles for software, but it’s not always obvious how to achieve this inside the Docker ecosystem. I’ll cover the details for each one so your server runs like a tight ship.

If you’re new to Raspberry Pi or Linux, I’ve got something that can help you right away!
Download my free Linux commands cheat sheet – it’s a quick reference guide with all the essential commands you’ll need to get things done on your Raspberry Pi. Click here to get it for free!

Letting Containers Go Down

Always set a restart policy so your containers come back automatically.

Let’s start with an easy one. I’ll sit down with some popcorn to watch Kung Fury on my smart TV, only to find that the movies on my Plex media server are unreachable for some reason. When I check my Docker container for Plex, I see that it’s down.

By default, Docker containers won’t restart automatically if the container fails due to an error or if you reboot the Raspberry Pi. If a container needs to be up 24/7, then it must be set explicitly.

To autostart a container at boot, and restart it if it fails, add this line to your config:
restart: always

‘Always’ can feel extreme when you’re tinkering, so try this next line instead. It reloads the container in most cases, except for when you manually stop it:
restart: unless-stopped

That’s usually enough to keep my Plex server humming along nicely. If you’ve got a mission-critical service, you can also go one step further and create a custom alert that notifies you if it goes down.

Something not working as expected?
You can get answers from real experts in minutes.
Get help with your setup

Cramming All of Your Services into One Dockerfile

Keep one service per container and one compose file per service.

I love Docker Compose because I think text file configurations make more sense, and I’m glad it’s slowly becoming the norm. But when I first started with it, I thought it would be brilliant to put all of my containers into one giant file. Easier to maintain, right?

Big mistake. One giant file ended up being actually more difficult to maintain:

  • You can’t easily control which containers get updated.
  • You can mix up settings and create port/volume conflicts.
  • If one container is misconfigured, all of your containers might not start.

The better solution for maintainability is to keep things separate: one service, one file.

For example, I make a separate folder for each container, like one for Immich and one for qbittorrent. Each folder contains the compose.yaml configuration for that container only.

Note: There may be a time and place to combine multiple services into one Docker file. For instance, if containers need to share volumes or share a virtual network to achieve a particular goal.

We use a configuration like this in our guide to Building the Ultimate Pi Security Stack. However, that’s for more advanced use cases, so beginners are safe sticking to the rule of thumb: one service, one file.

Hoarding Container Clutter

Regularly clean up unused containers, images, and volumes.

Docker doesn’t make it very intuitive to know what’s going on with old containers, duplicates, or other cruft still lying around.

To get a better idea, run this command:
docker system df
Or for more specifics:
docker system df -v

For instance, you might have leftovers from:

  • Old images not called by anything.
  • Stopped containers that aren’t used.
  • Inactive virtual networks from old setups.
  • Abandoned volumes.
  • Build cache remnants.

Container cruft can cause slowdowns because the Raspberry Pi has to scan through it all.

If it doesn’t spark joy, get rid of it. After I’m done with testing and have reached a stable setup that I want to keep, I’ll clean things out with this command:
docker system prune

There are commands to prune things more granularly (here), but I find the above syntax easy to remember and good enough for everyday use.

Tip: Command lines can be a pain to memorize. I put the essential Linux commands on a printable cheat sheet so you don't have to keep googling them. You can grab the PDF here if you want to save some time.

Allowing Containers to Hog Resources

Always define resource limits (CPU, RAM) for containers running on your Pi.

Most of my containers are polite and well-behaved, never eating more than their fair share. But now and then, I’ll host a really hungry boy—like PostgreSQL database—that hogs all of the CPU/RAM on my Raspberry Pi 5.

A container that’s allowed to run wild can choke your server, and this becomes more of a problem the more services you have. For instance, if your system throws an “Out of Memory” exception, it might start killing processes willy-nilly to free up resources. That’s too chaotic a setup.

To rein in resource hogs, set limits on what a container is allowed to use.
It’s easier to explain with an example, so here’s a config with the relevant parts in bold:

services:
  stress-test:
    image: jfleach/docker-arm-stress-ng:latest
    privileged: true

    cpus: 0.5
    mem_limit: 512m
    memswap_limit: 512m

    logging:
      driver: "local"
      options:
        max-size: 50m
        max-file: 3


    command: >
      --cpu 4
      --vm 2
      --vm-bytes 800m
      --vm-populate
      --timeout 60s
      --verbose

You can see from this command that I’ve successfully limited this container’s resource usage:
docker stats

Let’s go over what these limits do:

  • Processorcpus: 0.5 means the container is allowed up to 0.5 CPU cores.
  • Memorymem_limit: 512m, memswap_limit: 512m means the container can use 512MB RAM at most. Both lines are needed for this to work properly on a Raspberry Pi.
  • Logging – can be used to limit how much disk space is taken up by logs.
    • driver: local – specifies using our system’s log rotation features.
    • max-size: 50m – means the main log file can use 50MB of disk space at most.
    • max-file: 3 – rotate through 3 log files before deleting older ones.

Feel free to adjust the settings above to suit your needs. The truth is, I don’t set limits for every container I run because it might create performance bottlenecks. So I’ll check if a container is being a resource hog first, and if so, I’ll add some limits.

Tip: Docker doesn’t accurately report memory usage on the Raspberry Pi out of the box. If you need this feature, open /boot/firmware/cmdline.txt and add these parameters at the end of the long line there:
cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1

Creating Port Conflicts

Map unique host ports for every container to avoid conflicts.

When configuring Docker containers, it’s common to define a network port to connect to the service. Many services create a web panel, which defaults to port 80. If you have three different services set to use port 80, how will your Raspberry Pi know which one to serve?

To prevent conflicts, map different ports for each container so that they don’t overlap.
As an example, here’s the relevant portion from a Docker config for Pi-Hole adblocker:

pihole:
    container_name: pihole
    image: pihole/pihole:latest
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "80:80/tcp"
      - "443:443/tcp"

My immediate reaction when I see 80:80 and 443:443 is to modify them. These are commonly used ports that can conflict with other containers, like my Caddy web server. To remap the port, all you have to do is change the numbers to the left of the colon (:), like this:
6060:80/tcp
6443:443/tcp

Now, my Pi-Hole web panel is accessible on port 6060 instead. Sure, it’s not easy to remember, but I rarely visit there after the initial setup anyway, and a bookmark solves that easily enough.

Staying on Outdated Images

Regularly update your container images to stay secure and up to date.

Containers are designed to be isolated from your system, so it’s only natural that they won’t be touched during a regular update with APT. What ends up happening is that I load something like Home Assistant, forget about it for a year, and then leave it exposed to security exploits.

Let’s discuss a couple of ways you can make container updates more sustainable.

How to Update Docker Containers

Navigate to the folder where you stored the Docker compose.yaml file.
Then, run these commands to update the container:

docker compose pull
docker compose up

The first command downloads the newest image, while the second restarts the container using the updated image. If you prefer to update all of your containers regularly without having to do each one manually, you can create a script to automatically run these commands once a month.

Alternatively, you can do this from Portainer (Portainer is a graphical container manager that can be run as a container itself). Go to the Container Details page for the service, and click the Recreate button to update the container.

When to Use the ‘Latest’ Tag

I often use the ‘latest’ tag (e.g., image: wg-easy:latest) so that I always pull the newest version when I update a container. But there are situations where I’m running something mission-critical, like on a production server, and want something less bleeding edge.

When stability is needed, pin the Docker configuration to a specific image version.
For example, the container config for my WireGuard VPN server uses this image tag instead:
image: ghcr.io/wg-easy/wg-easy:14

The “:14” tag tells Docker to grab the last v14 release, which I know is already mature and only receives security updates. Had I used the ‘latest’ tag instead, it would grab the most recent v15 release, which is still going through major updates and might break my setup.


🛠 This tutorial doesn't work anymore? Report the issue here, so that I can update it!

Something not working? Don't waste hours going in circles. Ask in the RaspberryTips Community and get help from people who've already figured it out. Get unstuck now.

Forgetting to Save User Data

Configure volumes so your data is saved outside your containers.

Many containers need to store data, like settings or user files, just like any other program. But if you don’t tell it how to do this, then your stuff will eventually disappear. In a Docker configuration, the ‘volumes’ section tells the container where to save files.

There are different approaches to this, but here’s what I like to do:

  • Store each container in its own separate project folder.
  • Map volumes for user files relative to the project folder.

For example, I have a ~/containers folder to hold all of my Docker stuff. Inside, I have a subfolder for my project, paperless-ngx, and all it contains at first is its Docker configuration.

Here’s the relevant part from the compose.yaml:

paperless-ngx:
    image: ghcr.io/paperless-ngx/paperless-ngx:latest
    volumes:
      - data:/usr/src/paperless/data
      - media:/usr/src/paperless/media
      - ./export:/usr/src/paperless/export
      - ./consume:/usr/src/paperless/consume

The documentation states that the top two volumes (‘data’ & ‘media’) are app defaults for basic functionality, so let’s ignore them for instructional purposes. The bottom two volumes (‘export’ & ‘consume’), however, refer to user files, like my scanned documents, and where they should be saved. Without these lines, when the container goes down, the files will vanish into thin air!

By the way, by putting “./” in front, I’m telling Docker to map these volumes into the current folder—NOT in other random places on the Linux filesystem like /etc or /usr. This means all of my user files for paperless-ngx will be stored in the same place as its Docker configuration.

Why is that simpler? Because when I need to back up all of my containers, I zip up the ~/containers folder, and I’m done.

If I need to restore from backup, I bring the container up, and it already has everything it needs in one folder to start working again. It’s yet another reason I love using Docker: all of my work is reproducible, so I don’t need a perfect memory to reinstall things the way I had them before.

That’s all for today! With the right practices, you’ll have rock-solid containers that keep on truckin’ even when you’re not around.

If you want to learn more about Docker, don’t forget to check out our other tutorials:

If you’re new to Raspberry Pi or Linux, I’ve got something that can help you right away!
Download my free Linux commands cheat sheet – it’s a quick reference guide with all the essential commands you’ll need to get things done on your Raspberry Pi. Click here to get it for free!

Similar Posts