Data persistence strategy using ZFS and bind mounts for LXC containers.

Overview

Storage Type: ZFS with 4 pools Primary Data Pool: ssd_pool (3.62T) Service Data Location: /lxcdata/<service>/ on Proxmox host Container Mount Point: /data (inside LXC)

ZFS Pools

Pool Size Used Free Purpose
backup_pool 1.81T 762G 1.07T Backups, ISOs, templates
nvme_pool 928G 121G 807G Container root disks, VM disks
rpool 230G 14G 216G Proxmox boot/system
ssd_pool 3.62T 1.21T 2.42T Service data, VM data, media

ssd_pool Datasets

Dataset Mount Point Contents Compression
lxcdata /lxcdata Service bind mounts lz4
vmdata /vmdata VM images, Immich photos on
media /ssd_pool/media Films, series, videos off
databases /databases MSSQL lz4

Architecture

graph LR
    subgraph Host["Proxmox Host"]
        N8["/lxcdata/n8n/<br/>└── config/<br/>└── data/"]
        PL["/lxcdata/planka/<br/>└── postgres/"]
    end

    subgraph CT["LXC Container"]
        D1["/data/<br/>└── config/<br/>   └── n8n/"]
        D2["/data/<br/>└── postgres/"]
    end

    N8 --"bind mount"--> D1
    PL --"bind mount"--> D2

    style Host fill:#e3f2fd
    style CT fill:#fff3e0
    style N8 fill:#ffffff
    style PL fill:#ffffff
    style D1 fill:#ffffff
    style D2 fill:#ffffff

ZFS Benefits

  • Snapshots: Point-in-time recovery
  • Compression: Transparent storage savings (lz4 on most datasets)
  • Send/Receive: Efficient backup/replication
  • Data Integrity: Automatic checksum verification

Data Directory Structure

Each service has dedicated data on the host:

/lxcdata/
├── ai/                       # CT 124 - Ollama models
├── grav/                     # CT 123 - Flat-file content
├── jellyfin/                 # CT 125 - Media metadata
├── lanproxy/                 # CT 127 - Caddy config
├── n8n/                      # CT 120 - Workflow data
├── planka/                   # CT 122 - Project data
├── wordpress-db/             # CT 128 - MariaDB data
├── wordpress-jokegoudriaan/  # CT 129 - WordPress files
├── wordpress-kledingruil/    # CT 130 - WordPress files
└── wordpress-pgh/            # CT 131 - WordPress files

VM Data

/vmdata/
├── dump/                     # VM exports
├── images/                   # VM images
├── immich-photos/            # Immich photo storage
├── imports/                  # Imported VM images
├── laptop.qcow2              # ~296GB laptop image
├── private/                  # Private VM data
└── template/                 # VM templates

Creating Data Directory

When deploying a new service:

# On Proxmox host - create dataset
mkdir -p /lxcdata/<service>

# Add mount point to container config
pct set <CT_ID> -mp0 /lxcdata/<service>,mp=/data

Backup Strategy

What to Backup

Service Data: /lxcdata/<service>/ on host

  • Application data (databases, uploads, configs)

VM Data: /vmdata/ on host

  • Immich photos, VM images, templates

Proxmox Configs: Container/VM definitions

# LXC config backup
cat /etc/pve/lxc/<CT_ID>.conf > /backups/lxc-<CT_ID>-$(date +%F).conf

# VM config backup
cat /etc/pve/qemu-server/<VM_ID>.conf > /backups/vm-<VM_ID>-$(date +%F).conf

Backup Methods

ZFS Snapshots:

# Snapshot specific service data
zfs snapshot ssd_pool/lxcdata/<service>@before-update

# List snapshots
zfs list -t snapshot

# Restore snapshot
zfs rollback ssd_pool/lxcdata/<service>@before-update

File Backup:

# Service data to backup pool
rsync -av /lxcdata/<service>/ /backup_pool/lxcdata-backups/<service>/

# VM data to backup pool
rsync -av /vmdata/<service>/ /backup_pool/vmdata-backups/<service>/

Database Dumps: See Backup & Restore

Restore Procedures

From ZFS Snapshot

# Rollback to snapshot (destructive - reverts changes)
zfs rollback ssd_pool/lxcdata/<service>@snapshot-name

# Clone snapshot (non-destructive - creates copy)
zfs clone ssd_pool/lxcdata/<service>@snapshot-name ssd_pool/lxcdata/<service>-restore

From File Backup

# Restore service data
rsync -av /backup_pool/lxcdata-backups/<service>/ /lxcdata/<service>/

# Restart container to pick up restored data
pct restart <CT_ID>

Permissions

Unprivileged LXC UID Mapping

Unprivileged LXC containers map UIDs with +100000 offset:

Container UID Host UID Common Use
33 (www-data) 100033 Web servers
70 (postgres) 100070 PostgreSQL
999 (mysql) 100999 MariaDB
1000 101000 App-specific

Fixing Permissions

If service can't write to /data:

# On Proxmox host
chown -R <container_uid + 100000>:<container_gid + 100000> /lxcdata/<service>/<dir>

# Example: Fix WordPress permissions (www-data = 33 → 100033)
chown -R 100033:100033 /lxcdata/wordpress-jokegoudriaan/

Related

Note: Legacy directory cleanup completed on 2026-02-16 (~27GB freed). ZFS snapshot ssd_pool/lxcdata@before-cleanup-20260216 created as backup.

Related