How to Backup Data from a Docker Container in Portainer under Umbrel OS

Let's say you have an installed instance of Umbrel OS, whether on a physical or virtual machine. Inside Umbrel OS, you're running Portainer, and within it, various Docker images. In this article, we'll explore how to backup data from these images, as this process might seem non-trivial for many users. This is because Portainer under Umbrel OS uses a Docker-in-Docker (DinD) configuration:
sudo docker ps --filter="name=portainer"

What is Docker-in-Docker (DinD)?
DinD is an advanced Docker configuration that creates nested containerization environments. This approach is particularly useful in CI/CD pipelines and testing environments where isolated Docker instances are required. The technique allows for complete isolation of the inner Docker environment from the host system's Docker installation.
In other words Docker-in-Docker is a method of running a Docker daemon (dockerd
) inside a Docker container. In the official docker:<version>-dind
image, the Docker daemon automatically starts when the container launches through the wrapper script /entrypoint.sh dockerd
.
Why is this needed in Portainer?
Portainer can manage multiple Docker environments: a local Unix socket, remote Docker daemons over TCP, or an automatically launched DinD container. In our case, the docker:27.2.0-dind
image (container portainer_docker_1
) provides an isolated Docker environment that Portainer accesses as a "remote" host. This is convenient if you want to give Portainer a clean Docker daemon without affecting the host daemon. As we can see, Docker is running in portainer_docker_1
. Let's try to list the volumes inside this Docker instance:
sudo docker exec -it portainer_docker_1 sh -c "docker --host unix:///data/docker.sock volume ls"
DRIVER VOLUME NAME
local lamp_db_data
local lamp_web_data
local memos_memos-data
Here, we're launching a shell inside the portainer_docker_1
container and viewing the list of volumes from within it. Now, let's try to determine the path to these volumes on the actual machine, i.e., in Umbrel OS itself:
sudo docker inspect portainer_docker_1 \
--format '{{range .Mounts}}{{printf "%s → %s (type: %s, driver: %s)\n" .Source .Destination .Type .Driver}}{{end}}'
/home/umbrel/umbrel/app-data/portainer/data/docker → /data (type: bind, driver: )
/home/umbrel/umbrel/app-data/portainer/entrypoint.sh → /entrypoint.sh (type: bind, driver: )
/var/lib/docker/volumes/8e76c5ef4c92cdc5318b291f4b529e5169679a0c069b34d3e8a738172c673657/_data → /var/lib/docker (type: volume, driver: local)
And if we do something like:
sudo ls -la /home/umbrel/umbrel/app-data/portainer/data/docker/data/volumes
We'll see "folders" with data from all containers:
drwx-----x 11 root root 4096 May 20 10:21 .
drwx--x--- 13 root root 4096 May 20 10:21 ..
drwx-----x 3 root root 4096 Nov 24 2024 lamp_db_data
drwx-----x 3 root root 4096 Nov 24 2024 lamp_web_data
drwx-----x 3 root root 4096 Mar 8 22:47 memos_memos-data
Creating a Backup
Let's say we now want to backup a volume named memos_memos-data
and place it in the Downloads folder of Umbrel OS, so that the backup can be downloaded from the GUI. The easiest way to do this is with the following command:
sudo docker exec portainer_docker_1 sh -c \
"docker --host unix:///data/docker.sock run --rm -v memos_memos-data:/data alpine \
tar czf - -C /data ." \
> /home/umbrel/umbrel/home/Downloads/memos_memos-data-$(date +%F).tar.gz \
&& sudo chown 1000:1000 /home/umbrel/umbrel/home/Downloads/memos_memos-data-$(date +%F).tar.gz
Let's break down this command:sudo docker exec portainer_docker_1 sh -c "…"
– We enter the portainer_docker_1
container and execute a shell command sh -c "…"
. Inside this shell, we run:
docker --host unix:///data/docker.sock run --rm \
-v memos_memos-data:/data alpine \
tar czf - -C /data .
docker --host unix:///data/docker.sock
– We're addressing the internal Dockerd, which in DinD stores its socket at /data/docker.sock
.
run --rm -v memos_memos-data:/data alpine …
– We start a disposable (--rm
) container based on Alpine, in which the volume memos_memos-data
is mounted to /data
.
The Alpine Linux image is chosen here because it's extremely lightweight (around 5MB) and contains the essential tools needed for this operation. Using Alpine instead of a larger image like Ubuntu minimizes resource usage during the backup process.
tar czf - -C /data .
– Inside this Alpine container, we create a gzip archive to stdout (-f -
), packaging everything from /data
.
> /home/umbrel/.../Downloads/memos_memos-data-$(date +%F).tar.gz
– We redirect the stdout of the docker exec...tar
command on your host to a file named memos_memos-data-YYYY-MM-DD.tar.gz
in the Downloads folder.
&& sudo chown 1000:1000 …
– If the previous command executed without errors, we change the owner of the resulting archive to UID:GID 1000:1000.
The UID:GID 1000:1000 is typically assigned to the first non-root user created on Linux systems. In Umbrel OS, this corresponds to the 'umbrel' user. Setting these ownership permissions ensures that the backup file is accessible through the Umbrel OS web interface, which runs with the umbrel user's permissions.
As a result, you get a complete backup of the Docker volume memos_memos-data
, packaged in a tar.gz file with the appropriate permissions, all without leaving the host. In the end, our backup can be downloaded directly from the Umbrel OS interface.

I hope that now you won't have any problems saving data from Portainer under Umbrel OS.