⌂ Self-hosting

Data for containerised services: folders, volumes and permissions

Organising data for self-hosted services in containers: bind mounts or volumes, a clear folder layout, users and permissions, databases on local disks.

The beauty of containers is that you can delete and recreate them in seconds. The flip side is that anything not saved outside the container disappears with it. When a home service loses its configuration after an update, the cause is almost always the same: the data was not where you thought it was. Organising folders, volumes and permissions properly from day one makes every service easy to update, move and back up.

The principle: the container is disposable, the data is not

A container should be destroyable at any moment without losing anything. To achieve that, you need to separate three things:

  • The image: the program, pulled from the registry and always rebuildable.
  • The configuration: the compose file, environment variables and any settings files.
  • The data: databases, files uploaded by users, important caches, keys generated on first start.

Only the last two need to be saved and kept. If the compose file and the data folder are enough to rebuild a service from scratch, the organisation is right.

Bind mounts or managed volumes

There are two main ways to give a container persistent storage.

Feature Bind mount Managed volume
Where the data lives In a folder you choose In an area managed by the container engine
Visibility Browsable as normal files Needs a command or the internal path
Backup Just include the folder Needs careful handling
Permissions Fixed by hand Handled automatically

For a home server I almost always prefer bind mounts: everything that matters sits in a folder tree I can see, copy and include in a backup with a single line. Managed volumes remain useful for genuinely temporary data.

A predictable folder layout

A simple convention applied to every service saves you from having to remember where everything is:

/srv/services/
  immich/
    compose.yaml
    .env
    data/
    db/
  freshrss/
    compose.yaml
    data/

Each service has its own folder containing the compose file, the variables file and the data subfolders. Moving a service to another machine becomes mechanical: stop it, copy the folder, start it on the other side. If the media files are large (photos, films) it is normal to keep them on a different disk, but the reference must be written out clearly in the compose file.

Users and permissions

The most common problem with bind mounts is permissions. The process inside the container runs with a certain user ID (UID) and group ID (GID); if the folder on the host belongs to another user, the service cannot write and often fails with unhelpful errors.

Some practical solutions:

  1. Read the image documentation: many images accept variables such as PUID and PGID to choose the user they run as.
  2. Create a dedicated user on the host for services and give it ownership of the data folders.
  3. Avoid running everything as root: it works, but a bug or vulnerability in the service would have full access to the mounted files.
  4. Mount read-only whatever the service only needs to read, by adding :ro to the path. A media server, for example, has no need to modify the films.

Databases: always on a local disk

Many services use SQLite or PostgreSQL. Databases are sensitive to how the file system handles locking and writes to disk. Putting the database folder on a network share (NFS or SMB) is a classic source of corruption that is hard to diagnose, especially with SQLite.

The rule I follow is simple: databases on a local disk, preferably an SSD; large, less critical files can also live on a NAS. If the service allows it, it is useful to split the two into separate folders precisely so you can place them in different locations.

Think about backups from the start

A good layout makes backups almost automatic. With everything under /srv/services, there is only one path to include. Two precautions remain:

  • Databases should not be copied while they are writing: stop the service during the copy or export a dump before the backup.
  • The .env file often contains passwords and keys: it must be saved, but in an encrypted backup, and never published in a repository.

A test to run right away

To check that everything is in order, you can simulate a disaster on an unimportant service: stop it, remove the container and the image, then recreate it from its folder alone. If it comes back with users, settings and data intact, the layout works. If something is missing, better to find out now than during a real failure.

In short

  • The container can be thrown away; configuration and data must live outside it.
  • Bind mounts in a predictable folder layout simplify backups and migrations.
  • UID, GID and read-only mounts solve most permission problems.
  • Databases belong on a local disk, not on network shares.
  • Recreating a service from scratch is the best test of whether your data is safe.
MM
Marco Marzaduri

Marco Marzaduri works in cybersecurity, digital forensics and web development. A computer science graduate, he combines rigorous technical analysis with attention to the people who use technology.

Read also