Persistence for files stored within OverlayFS file system

Hello,

I’m new to Docker and need some help with a pre-compiled Docker image installation in combination with the OverlayFS file system. The associated docker service periodically writes data to a file, that will be also used at startup. I need persistent storage for this file, but don’t know how to achieve it?

Here are the details of my OpenSprinkler Weather Server Docker installation:

OpenSprinkler is a Garden Watering System that may be controlled by weather data. The weather data is thereby handled by a Weather Server, which also allows for a local Raspberry PI installation. As I’m already running WeeWX as a weather source on a Raspberry Pi 3, it might be helpful to run the Weather Service Version 3.0.2 on the same Raspberry PI in a Docker Installation. I’m using Debian GNU/Linux 12 (bookworm) and Docker Version 29.1.3.

There is a precompiled OpenSprinkler-Weather Server available at GitHub. The GitHub repository automatically publishes an up-to-date image, which I pulled as follows:

pi@raspberrypi:~ $ docker pull Package weather-server · GitHub

Thereafter, I created the /weather directory and created the following compose.yaml file.

services:
weatherserver:
image: Package weather-server · GitHub
container_name: WeatherServer
restart: unless-stopped
environment:
HOST: 0.0.0.0
PORT: 3000
GEOCODER: GoogleMaps
GOOGLE_MAPS_API_KEY: XXXXXXX
WEATHER_PROVIDER: local
PWS: WU
LOCAL_PERSISTENCE: true
TZ: Europe/Vienna
ports:
- 3000:3000
working_dir: /weather

However, I omitted any “volumes definition”, for reason that I don’t know how to define it?

I started the WeatherServer with Docker Compose:

pi@raspberrypi:/weather $ docker compose up
[+] up 2/2
Network weather_default Created 0.3s
Container WeatherServer Created 0.5s
Attaching to WeatherServer
WeatherServer |
WeatherServer | > os-weather-service@3.0.2 start
WeatherServer | > node dist/index.cjs
WeatherServer |
WeatherServer | OpenSprinkler Weather Service now listening on 0.0.0.0:3000
WeatherServer | OpenSprinkler Weather Service now listening for local weather stream
WeatherServer | Loaded baseline ETo data.

It works as expected and is still waiting to get sufficient weather data, which takes at least 24 hours to complete.

OpenSprinkler Weather Service v3.0.2
&errCode=10&scale=100

The WeatherServer creates and periodically writes the retrieved weather data to an observations.json file. New weather measurements are added every 30 minutes to this file. The file is stored within the OverlayFS file system. It can be copied, e.g. to the /weather working directory with:

sudo docker cp WeatherServer:./weather/observations.json /weather/observations.json

The question is now, what “volumes definition” is valid for my setup? I would like to have the observations.json file permanently in place, to store the weather observations and survive docker restarts and updates. Sorry for the long explanation. I have read through all the tutorials and searched the web about this problem. But, I have no ideas yet how to so solve it?

Thanks in advance.

Franz

The Docker docs explain it all.

You can use Docker volumes (doc), those survive a restart and a re-create. You can use a Docker bind-mount (doc), that mounts a host folder into the container. Both solutions are supported in compose.

I personally prefer bind mounts. You don’t have the risk of losing data when accidentally killing the volume. Also I find that data on host is easier to backup.

Thanks for your help and the hint to use bind mounts. I worked through the docs, but still have some understanding problems. I added the following lines to the compose.yaml file:

volumes:
  - type: bind
    source: /weather/observations.json
    target: WeatherServer:/weather/observations.json

This results in the following error:

pi@raspberrypi:/weather $ docker compose up
WARN[0000] No services to build
[+] up 2/2
:check_mark: Network weather_default Created 0.1s
✘ Container WeatherServer Error response from daemon: invalid volume specification: ‘/weather/observations.json:WeatherServer:/weather/observations.json:rw’ 0.4s
Error response from daemon: invalid volume specification: ‘/weather/observations.json:WeatherServer:./weather/observations.json:rw’

The question is now, how to address the file observations.json stored in the

/var/lib/docker/rootfs/overlayfs/d20707efeb8e01a811ead70d1c359c90b3291a331697ff798190a76ee9f2b6ce/weather

overlayFS directory.

The Id is only temporarily available and changes with every start of the container! Maybe I can’t see the forest for the trees. But I’m lost here and need additional help.

Thanks in advance

Franz

You used the long syntax for a bind, so target should only define the target folder inside the container. The part WeatherServer: doesn’t belong into the long syntax definition of a target folder.

Note: with binds, the inode for the source path on the host is resolved at point in time when the container is started, and this inode is mounted into the target path inside the container. If you bind a single file or a folder that is a mount point gets re-mounted, changes on the host won’t be visible inside the container. If a single file bind is edited/overridden inside the container, it will have a new inode and won’t be written back to the file on the host (because both don’t share the same inode anymore)

Thanks for the detailed explanation. So Binds are not the way to go for me, as I need a persistent data storage. I’m back to the Volumes now and changed my compose.yaml file as follows:

#YAML configuration file, known as the Compose file, to configure the WeatherServer services.
services:
 weatherserver:
    image: ghcr.io/opensprinkler/weather-server:master
    container_name: WeatherServer
    restart: unless-stopped
    environment:
      HOST: 0.0.0.0
      PORT: 3000
      GEOCODER: GoogleMaps
      GOOGLE_MAPS_API_KEY: XXXXXXX
      WEATHER_PROVIDER: local
      PWS: WU
      #LOCAL_PERSISTENCE=true will cause the WeatherServer to write the received weather history
      #to disk every 30 minutes so that when it restarts it can pick up from where it left off.
      LOCAL_PERSISTENCE: true
      TZ: Europe/Vienna
    ports:
      - 3000:3000
    working_dir: /weather

    #The Weather Service needs at least 23 hours’ worth of data to create a meaningful water scale.
    #The "WeatherData" volume is used as persistent data store implemented by the container engine.
    volumes:
       - WeatherServer:/weather
volumes:
  WeatherServer:
      name: "WeatherData"

The “WeatherData” volume is now persistent available at:

/var/lib/docker/volumes/WeatherData/_data

And the corresponding observations.json file is also still available after container restarts.

Thanks

Franz

Binds are also persistent when you use a folder as bind. If you had used the parent folder of your json file as bind, it would also have worked.

Everything underneath /var/lib/docker is managed by the docker engine. Usually there is no reason to access anything inside that folder. If you feel the need to access it, then probably you are doing something, that is meant to be done differently.

Technically Named volumes with default configuration, are nothing else than folders underneath the docker data root folder that are managed by docker. The folder /var/lib/docker/volumes/<volume name>/_data_data is bind mounted (by inode) into the container path.

The key to your “it persists” experience is that a folder is mounted into the container path, and not just a file.

Ok. I think I have a better understanding of the Docker storage principles now. There is no need to access the observation.json file by another app. It stores the weather data for the last 24 hours and is only looked at for visual inspection. If everything works, there is no need to look at it anymore.

That means using Docker volumes should be ok to survive a restart and a re-create. I think I will leave it as is. Nevertheless, I will work through the docker docs to learn more about containerization.

Many thanks again.

Franz

Bind mounts are perfectly fine for permanent storage on host drive. We run multiple SaaS solutions that way.

It just doesn’t work well with USB drives or network drives that can get re-connected. Then you need to bind mount a fixed host parent folder.