Storage Configuration

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

PoolSizeUsedFreePurpose
backup_pool1.81T762G1.07TBackups, ISOs, templates
nvme_pool928G121G807GContainer root disks, VM disks
rpool230G14G216GProxmox boot/system
ssd_pool3.62T1.21T2.42TService data, VM data, media

ssd_pool Datasets

DatasetMount PointContentsCompression
lxcdata/lxcdataService bind mountslz4
vmdata/vmdataVM images, Immich photoson
media/ssd_pool/mediaFilms, series, videosoff
databases/databasesMSSQLlz4

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
├── syncthing/                # CT 121 - File sync (orphaned, CT deleted)
├── 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 UIDHost UIDCommon Use
33 (www-data)100033Web servers
70 (postgres)100070PostgreSQL
999 (mysql)100999MariaDB
1000101000App-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/

Cleanup & Action Items

The following items were discovered during verification and need attention:

Orphaned/Legacy Directories in /lxcdata

DirectorySizeStatusAction Needed
syncthing/-OrphanedCT 121 deleted - safe to remove
wp-jokegoudriaan/3.3GDuplicateOld WordPress data - safe to remove
wp-kledingruil/191MDuplicateOld WordPress data - safe to remove
wp-pgh/8.9GDuplicateOld WordPress data - safe to remove
docker-web/-OldContains old grav/nginx - evaluate
plex/-OldReplaced by jellyfin - safe to remove
srvweb1/-OldContains old nginx/www - evaluate
mariadb/EmptyUnusedEmpty directory - safe to remove
postgresql/-OldOld postgresql data - evaluate

Total potential space savings: ~12.4GB

Documentation Updates Needed

  • Document CT 121 (syncthing) status - delete doc or mark as removed?
  • Add note about CT 126 (cloudflared) being stateless (no data mount)
  • Document /vmdata usage for VM images and Immich
  • Document /ssd_pool/media for media library
  • Document /databases for MSSQL

Service Docs to Verify

  • CT 126 (tunnel/cloudflared) - verify it’s stateless in docs
  • VM 108 (immich) - confirm photo storage location