→Home Lab | →Back | →Gateway

Proxmox Storage Server Install Guide

Smart Home Edge Systems

(smart-home-edge-systems.us)

Infotainment architecture

The following architecture source is retained at the beginning of this guide exactly as supplied. It is the build target for the installation steps that follow.

=========================================================================
 Barnes Home Lab Configuration
 * * * * Infotainment Module * * * *
 PROXMOX VE SYSTEM (10C / 16T | 32GB RAM)
 Proxmox VE 9.2.3 / Dedicated Ethernet / Intel QuickSync
 Storage: 1TB NVMe OS / Central Storage Array Mounts
=========================================================================

 [ PROXMOX VE HOST ] --------- Reserve 2GB RAM / 2 Threads for overhead ]
 │ 192.168.86.235
 [ Infotainment HOST (NUT Server) ]
 │    * Monitors UPS hardware over USB
 │    * Sends status to local/remote clients
 ├──► [ LXC 100 ] SAMBA-NAS (Central Storage Gateway)
 │    ├── CPU: 2 Cores (vCPUs)
 │    ├── RAM: 1 GB (Scalable)
 │    ├── STO: 16 GB NVMe (OS)
 │    ├── SHARE EXPORTS (SMB/NFS):
 │    │   ├── /mnt/storage/media        ---> Shared across Plex, Jellyfin, Emby
 │    │   ├── /mnt/storage/audiobooks   ---> Exported to Audiobookshelf
 │    │   ├── /mnt/storage/documents    ---> Exported to Paperless-NGX / Nextcloud
 │    │   ├── /mnt/storage/photos       ---> Exported to Immich / Nextcloud
 │    │   ├── /mnt/storage/downloads    ---> Exported to Deluge
 │    │   ├── /mnt/storage/3d_assets    ---> Exported to Manyfold
 │    │   └── homelab-backups/configs   ---> Used for Hybrid Backups
 │    │       ├── compute-node/
 │    │       │   ├── haos/                  # Automated HA configuration backups
 │    │       │   ├── open-webui/            # Compose files & volumes
 │    │       │   └── pihole/                # Teleporter export archives
 │    │       ├── infotainment-node/
 │    │       │   ├── postgres-dumps/        # Nightly pg_dump SQL files
 │    │       │   ├── nginx/                 # Nginx/NPM configuration files
 │    │       │   └── docker-stacks/         # VM 113 compose files & .env files
 │    │       └── cold-sync-status.log
 │    └── 192.168.86.234 - Primary LAN Segment (vmbr0) eth0
 ├──► [ LXC 101 ] POSTGRES-DB
 │    ├── CPU: 4 Cores (vCPUs)
 │    ├── RAM: 4 GB (Dedicated)
 │    ├── STO: 32 GB NVMe
 │    ├── BIND MP: mp0: /mnt/pve/db_data,mp=/var/lib/postgresql/data
 │    └── 10.0.20.10 - Isolated Backend Segment (vmbr1) eth0
 ├──► [ LXC 102 ] MEDIA-PLEX
 │    ├── CPU: 6 Cores (vCPUs) ---> (Intel QuickSync Passthrough)
 │    ├── RAM: 4 GB
 │    ├── STO: 20 GB NVMe
 │    ├── BIND MP: mp0: /mnt/pve/shares/media,mp=/data/media,ro=1
 │    └── 192.168.86.236 - Primary LAN Segment (vmbr0) eth0
 ├──► [ LXC 103 ] MEDIA-JELLYFIN
 │    ├── CPU: 6 Cores (vCPUs) ---> (Intel QuickSync Passthrough)
 │    ├── RAM: 4 GB
 │    ├── STO: 20 GB NVMe
 │    ├── BIND MP: mp0: /mnt/pve/shares/media,mp=/data/media,ro=1
 │    └── 192.168.86.237 - Primary LAN Segment (vmbr0) eth0
 ├──► [ LXC 104 ] MEDIA-EMBY
 │    ├── CPU: 4 Cores (vCPUs)
 │    ├── RAM: 2 GB
 │    ├── STO: 20 GB NVMe
 │    ├── BIND MP: mp0: /mnt/pve/shares/media,mp=/data/media,ro=1
 │    └── 192.168.86.238 - Primary LAN Segment (vmbr0) eth0
 ├──► [ LXC 105 ] AUDIOBOOKSHELF
 │    ├── CPU: 2 Cores (vCPUs)
 │    ├── RAM: 1 GB
 │    ├── STO: 10 GB NVMe
 │    ├── BIND MP: /mnt/pve/shares/audiobooks,mp=/data/audiobooks
 │    └── 192.168.86.239 - Primary LAN Segment (vmbr0) eth0
 ├──► [ LXC 106 ] NEXTCLOUD
 │    ├── CPU: 4 Cores (vCPUs)
 │    ├── RAM: 4 GB
 │    ├── STO: 32 GB NVMe
 │    ├── BIND MP: mp0: /mnt/pve/shares/documents,mp=/var/www/html/data
 │    └── 192.168.86.240 - Primary LAN Segment (vmbr0) eth0
 ├──► [ LXC 107 ] IMMICH
 │    ├── CPU: 6 Cores (vCPUs)
 │    ├── RAM: 6 GB
 │    ├── STO: 32 GB NVMe
 │    ├── BIND MP: mp0: /mnt/pve/shares/photos,mp=/usr/src/app/upload
 │    └── 192.168.86.241 - Primary LAN Segment (vmbr0) eth0
 ├──► [ LXC 108 ] PAPERLESS-NGX
 │    ├── CPU: 2 Cores (vCPUs)
 │    ├── RAM: 2 GB
 │    ├── STO: 16 GB NVMe
 │    ├── BIND MP: mp0: /mnt/pve/shares/documents/paperless,mp=/usr/src/paperless/consume
 │    ├── BIND MP: mp1: /mnt/pve/shares/documents/archive,mp=/usr/src/paperless/media
 │    └── 192.168.86.242 - Primary LAN Segment (vmbr0) eth0
 ├──► [ LXC 109 ] POSTGRES-FE
 │    ├── CPU: 2 Cores (vCPUs)
 │    ├── RAM: 512 MB
 │    ├── STO: 8 GB NVMe
 │    └── 192.168.86.243 - Primary LAN Segment (vmbr0) eth0
 ├──► [ LXC 110 ] DELUGE (VPN)
 │    ├── CPU: 2 Cores (vCPUs)
 │    ├── RAM: 1 GB
 │    ├── STO: 16 GB NVMe
 │    ├── BIND MP: mp0: /mnt/pve/shares/downloads,mp=/data/downloads
 │    └── 192.168.86.244 - Primary LAN Segment (vmbr0) eth0
 ├──► [ LXC 112 ] LINUX-GSM
 │    ├── CPU: 8 Cores (vCPUs)
 │    ├── RAM: 4 GB
 │    ├── STO: 100 GB NVMe
 │    ├── BIND MP: mp0: /mnt/pve/shares/game_backups,mp=/home/lgsm/backups
 │    └── 192.168.86.245 - Primary LAN Segment (vmbr0) eth0
 └──► [ VM 113 ] DOCKER WEB STACK
      ├── CPU: 4 Cores (vCPUs)
      ├── RAM: 4 GB (Dedicated)
      ├── STO: 32 GB NVMe
      ├── SMB/NFS VM MOUNTS:
      │   ├── /mnt/shares/3d_assets ---> Mounted into Manyfold container
      │   └── /mnt/shares/backups   ---> Mounted for Uptime Kuma/Netbox exports
      ├── Docker Container: Pi-hole Backup (Port 53 / 80)
      ├── Docker Container: Gravity-Sync   (Syncs with Compute Pi-hole at 192.168.86.254)
      ├── Docker Container: Manyfold       (Port 3232:3232 | Volume: /mnt/shares/3d_assets:/libraries)
      ├── Docker Container: Homebox        (Port 7745:7745)
      ├── Docker Container: Netbox         (Port 8000:8000)
      ├── Docker Container: Grocy          (Port 9192:80)
      ├── Docker Container: Mealie         (Port 9925:9000)
      ├── Docker Container: Uptime Kuma    (Port 3001:3001)
      ├── 192.168.86.233 - Primary LAN Segment (vmbr0) eth0
      └── Watchtower Container #2
          └── Monitors: Gravity-Sync, Manyfold, Homebox, Netbox, Grocy,
                        Mealie, Uptime Kuma, Pi-hole Backup

How to use this guide

Perform the steps in order. Do not create all containers first and tune them later. Each CT has a create, configure, tune, validate, and checkpoint phase. Stop at a failed checkpoint and repair it before continuing.

Commands marked DESTRUCTIVE can erase disks or delete containers. Replace values in angle brackets.

  • Run Proxmox host commands on the host.

  • LXC commands inside the named CT

  • Docker commands inside VM 113.

Build variables and inventory

Create an installation worksheet before powering on the host:

Variable Target value
Proxmox hostname infotainment
Proxmox management IP 192.168.86.235/24
Gateway <LAN-GATEWAY>
DNS <DNS-1>, <DNS-2>
Primary bridge vmbr0
Backend bridge vmbr1
Storage disk IDs <record by-id paths>
NVMe storage ID <record by-id path>
Backup share /``mnt``/storage/``homelab``-backups
Proxmox version 9.2.3 target, verify current release before install

Record disk model and serial values with:

lsblk -o NAME,SIZE,MODEL,SERIAL,FSTYPE,MOUNTPOINTS
ls -l /dev/disk/by-id/

Step 1: Create the Proxmox installation USB

  1. Download the Proxmox VE ISO from the official Proxmox download page.

  2. Download the SHA-256 checksum from the same release page.

  3. On Windows, open PowerShell and calculate the ISO checksum:

    Get-FileHash .\proxmox-ve_*.iso -Algorithm SHA256

  4. Compare the result with the published checksum character-for-character.

  5. Insert an 8 GB or larger USB drive and close every Explorer window using it.

  6. In Rufus, select the ISO and the verified USB drive.

  7. Select DD Image mode if Rufus asks which write mode to use.

  8. Write the image, safely eject it, and label it PROXMOX-INSTALL.

The Proxmox installer USB is a hybrid ISO. Do not manually partition it first. Writing the ISO replaces the partition table and creates the boot layout required by the release.

For a separate provisioning USB, use GPT with a 512 MiB FAT32 EFI partition, an 8 GiB or larger exFAT provisioning partition, and a remaining exFAT data partition. Do not store the only credential or backup copy on removable media.

Step 2: Boot and install Proxmox VE

  1. Connect the installer USB and wired Ethernet.
  2. Power on the host and open the UEFI one-time boot menu.
  3. Select the USB entry marked UEFI.
  4. Choose Install Proxmox VE.
  5. Select the system NVMe only. Confirm its model and serial. Do not select the bulk storage disk.
  6. Select the target filesystem. For this single-node design, use the filesystem chosen in the storage worksheet; do not enable ZFS without a deliberate disk layout and memory budget.
  7. Set timezone, keyboard, root password, and administrative email.
  8. Set hostname infotainment and management IP 192.168.86.235/24, using the real gateway and DNS values.
  9. Review the final summary and begin installation.
  10. Remove the USB when the installer reboots.
  11. Browse to https://192.168.86.235:8006 and sign in as root.

Checkpoint: confirm the web interface loads, the hostname resolves, and the physical network link is stable before continuing.

Step 3: Update and baseline the Proxmox host

Run on the host:

pveversion -v
hostnamectl
ip -br addr
systemctl --failed
apt update
apt full-upgrade
reboot

After reboot:

pvesm status
pvesh get /nodes/$(hostname)/storage
lsblk -o NAME,SIZE,MODEL,SERIAL,FSTYPE,MOUNTPOINTS

Enable IOMMU only when GPU or device passthrough is required. Confirm the firmware setting first, then follow the Proxmox release documentation for the correct kernel parameter. Do not enable passthrough on a device still needed by the host.

Step 4: Partition and mount the storage array

The system NVMe is for Proxmox, CT operating systems, databases, metadata, and caches. The storage array is for media, photos, documents, downloads, backups, and other bulk data.

DESTRUCTIVE: replace the device path only after verifying model and serial three times.

lsblk -o NAME,SIZE,MODEL,SERIAL
wipefs -n /dev/disk/by-id/<DATA-DISK-ID>
parted /dev/disk/by-id/<DATA-DISK-ID> --script mklabel gpt
parted /dev/disk/by-id/<DATA-DISK-ID> --script mkpart primary ext4 1MiB 100%
mkfs.ext4 -L bulkdata /dev/disk/by-id/<DATA-DISK-ID>-part1
mkdir -p /mnt/storage
blkid /dev/disk/by-id/<DATA-DISK-ID>-part1

Add the UUID to /``etc``/``fstab:

UUID=<DATA-UUID> /mnt/storage ext4 defaults,noatime 0 2

Then mount and create the architecture directories:

mount -a
findmnt /mnt/storage
mkdir -p /mnt/storage/{media,audiobooks,documents,photos,downloads,3d_assets,game_backups,homelab-backups/configs}
mkdir -p /mnt/storage/homelab-backups/configs/{compute-node,infotainment-node}

For multiple disks, stop and choose ZFS, hardware RAID, or another design before formatting anything. Backups are required regardless of the redundancy choice.

Step 5: Create the host bridges

Configure vmbr0 for the primary LAN and vmbr1 for the isolated database/backend segment. Use the Proxmox GUI or the host network configuration, then verify before creating CTs:

ip -br link
ip -br addr
bridge link
ping -c 3 <LAN-GATEWAY>

The target addresses are:

Bridge Purpose Example
vmbr0 Primary LAN CT 100, 102-110, VM 113
vmbr1 Backend isolation CT 101 at 10.0.20.10

Step 6: Install CT 100 - Samba-NAS

Use the official Community Scripts catalog, inspect the current script, and select an unprivileged Debian LXC. Use CTID 100, hostname samba-``nas, 2 vCPUs, 1 GiB RAM, 16 GiB NVMe root disk, and a fixed address of 192.168.86.234/24 on vmbr0.

Example command shape; use the exact current catalog URL and script name after review:

bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/samba.sh)"

On the host, stop the CT and add the storage bind mount:

pct stop 100
pct set 100 --cores 2 --memory 1024 --swap 512 --onboot 1
pct set 100 --startup order=10,up=30,down=30
pct set 100 --mp0 /mnt/storage,mp=/mnt/storage
pct start 100

Inside CT 100:

apt update && apt full-upgrade
apt install -y samba acl
smbpasswd -a <SMB-USER>

Create shares for media, audiobooks, documents, photos, downloads, 3d_assets, and homelab``-backups. Do not expose the Nextcloud data directory as a general-purpose share. Apply least-privilege users and groups.

pct exec 100 -- test -d /mnt/storage/media
pct exec 100 -- systemctl is-active smbd
smbclient -L //192.168.86.234 -U <SMB-USER>

Checkpoint: copy a disposable test file through SMB and confirm it is readable from the host and from the intended application CT.

Step 7: Install CT 101 - PostgreSQL

Use a current Debian LXC or the reviewed PostgreSQL Community Script. Set CTID 101, hostname postgres-db, 4 vCPUs, 4 GiB RAM, 32 GiB NVMe root disk, and 10.0.20.10/24 on vmbr1. Restrict the firewall to application clients.

bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/postgresql.sh)"

The active PostgreSQL directory is on the fast database storage, not the bulk USB share. If a dedicated host mount is used, stop the CT and add it only after formatting and mounting the host storage:

pct stop 101
pct set 101 --cores 4 --memory 4096 --swap 1024 --onboot 1
pct set 101 --startup order=20,up=45,down=45
pct set 101 --mp0 /mnt/pve/db_data,mp=/var/lib/postgresql/data
pct start 101

Inside CT 101, create one database and one least-privilege role per application. Never use the PostgreSQL superuser in application connection strings:

sudo -u postgres psql
CREATE ROLE <APP_USER> LOGIN PASSWORD '<LONG-RANDOM-PASSWORD>';
CREATE DATABASE <APP_DB> OWNER <APP_USER>;
\q

Configure pg_hba.conf for only the required backend and application addresses. Configure nightly pg_dump files into /mnt/storage/homelab-backups/configs/infotainment-node/postgres-dumps through a controlled mount or backup job.

pct exec 101 -- systemctl is-active postgresql
pct exec 101 -- sudo -u postgres psql -c 'select version();'
pg_dump --format=custom --file=/tmp/postgres-check.dump <APP_DB>

Step 8: Install CT 102 - Plex

Create CT 102 with 6 vCPUs, 4 GiB RAM, 20 GiB NVMe, fixed address 192.168.86.236/24, and vmbr0. Use the reviewed Community Script:

bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/plex.sh)"

Tune and mount media:

pct stop 102
pct set 102 --cores 6 --memory 4096 --swap 1024 --onboot 1
pct set 102 --startup order=40,up=60,down=30
pct set 102 --mp0 /mnt/storage/media,mp=/data/media,ro=1
pct start 102

If Intel Quick Sync is available, expose /dev/``dri only after confirming the host device and required group mapping. Inside the CT, verify /dev/``dri, then enable hardware transcoding in Plex. Keep metadata and transcode cache on the NVMe root disk. Disable unnecessary duplicate library analysis while Jellyfin and Emby are being configured.

Step 9: Install CT 103 - Jellyfin

Create CT 103 with 6 vCPUs, 4 GiB RAM, 20 GiB NVMe, fixed address 192.168.86.237/24, and vmbr0:

bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/jellyfin.sh)"
pct stop 103
pct set 103 --cores 6 --memory 4096 --swap 1024 --onboot 1
pct set 103 --startup order=40,up=60,down=30
pct set 103 --mp0 /mnt/storage/media,mp=/data/media,ro=1
pct start 103

Set the transcode directory to local NVMe, mount the library read-only, verify /dev/``dri, and enable Quick Sync only after a test stream confirms it is actually used.

Step 10: Install CT 104 - Emby

Create CT 104 with 4 vCPUs, 2 GiB RAM, 20 GiB NVMe, fixed address 192.168.86.238/24, and vmbr0:

bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/emby.sh)"
pct stop 104
pct set 104 --cores 4 --memory 2048 --swap 512 --onboot 1
pct set 104 --startup order=40,up=60,down=30
pct set 104 --mp0 /mnt/storage/media,mp=/data/media,ro=1
pct start 104

Choose one media server as the primary metadata scanner. Disable preview thumbnails, duplicate scans, and automatic artwork generation in the others until I/O and storage capacity have been measured.

Step 11: Install CT 105 - Audiobookshelf

Create CT 105 with 2 vCPUs, 1 GiB RAM, 10 GiB NVMe, fixed address 192.168.86.239/24, and vmbr0:

bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/audiobookshelf.sh)"
pct stop 105
pct set 105 --cores 2 --memory 1024 --swap 512 --onboot 1
pct set 105 --startup order=40,up=45,down=30
pct set 105 --mp0 /mnt/storage/audiobooks,mp=/data/audiobooks,ro=1
pct start 105

Put the application database and cache on NVMe. Add the read-only library in the web UI, scan once, and validate playback before enabling scheduled scans.

Step 12: Install CT 106 - Nextcloud

Create CT 106 with 4 vCPUs, 4 GiB RAM, 32 GiB NVMe, fixed address 192.168.86.240/24, and vmbr0:

bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/nextcloud.sh)"
pct stop 106
pct set 106 --cores 4 --memory 4096 --swap 1024 --onboot 1
pct set 106 --startup order=40,up=60,down=45
pct set 106 --mp0 /mnt/storage/documents,mp=/var/www/html/data
pct start 106

Create the PostgreSQL database and Redis configuration before finishing the Nextcloud wizard. Nextcloud owns its data directory. Do not expose that directory through the general Samba share. Set background jobs to cron and run the maintenance commands as the web user.

Validate with a browser upload, desktop sync, and a restore of a test file. Confirm the data directory permissions and database connectivity.

Step 13: Install CT 107 - Immich

Create CT 107 with 6 vCPUs, 6 GiB RAM, 32 GiB NVMe, fixed address 192.168.86.241/24, and vmbr0:

bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/immich.sh)"
pct stop 107
pct set 107 --cores 6 --memory 6144 --swap 1024 --onboot 1
pct set 107 --startup order=40,up=60,down=45
pct set 107 --mp0 /mnt/storage/photos,mp=/usr/src/app/upload
pct start 107

Keep Immich database, thumbnails, and machine-learning cache on NVMe. Start with CPU processing. Add supported acceleration only after validating the host device, LXC permissions, and the application release requirements. Import a test photo and confirm the original file remains intact.

Step 14: Install CT 108 - Paperless-NGX

Create CT 108 with 2 vCPUs, 2 GiB RAM, 16 GiB NVMe, fixed address 192.168.86.242/24, and vmbr0:

bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/paperless-ngx.sh)"
pct stop 108
pct set 108 --cores 2 --memory 2048 --swap 512 --onboot 1
pct set 108 --startup order=40,up=45,down=30
pct set 108 --mp0 /mnt/storage/documents/paperless,mp=/usr/src/paperless/consume
pct set 108 --mp1 /mnt/storage/documents/archive,mp=/usr/src/paperless/media
pct start 108

Create the PostgreSQL role and Redis namespace first. Drop a disposable PDF into consume, confirm indexing, and confirm the archive path is not being modified by unrelated SMB users.

Step 15: Install CT 109 - PostgreSQL Frontend

Create CT 109 with 2 vCPUs, 512 MiB RAM, 8 GiB NVMe, fixed address 192.168.86.243/24, and vmbr0. Use the reviewed Community Script for the chosen database frontend, or install the frontend manually if the catalog does not support the required product.

bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/pgadmin.sh)"
pct stop 109
pct set 109 --cores 2 --memory 512 --swap 256 --onboot 1
pct set 109 --startup order=30,up=30,down=30
pct start 109

Restrict access to administrators. Never put the PostgreSQL password in a public URL, shared document, or world-readable environment file.

Step 16: Install CT 110 - Deluge VPN

Create CT 110 with 2 vCPUs, 1 GiB RAM, 16 GiB NVMe, fixed address 192.168.86.244/24, and vmbr0:

bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/deluge-vpn.sh)"
pct stop 110
pct set 110 --cores 2 --memory 1024 --swap 512 --onboot 1
pct set 110 --startup order=40,up=45,down=30
pct set 110 --mp0 /mnt/storage/downloads,mp=/data/downloads
pct start 110

Use this only for content you are authorized to download. Confirm the VPN kill switch, DNS behavior, and that traffic cannot bypass the tunnel. Test a harmless transfer and verify the download path.

Step 17: Install CT 112 - Linux-GSM

Create CT 112 with 8 vCPUs, 4 GiB RAM, 100 GiB NVMe, fixed address 192.168.86.245/24, and vmbr0:

bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/linuxgsm.sh)"
pct stop 112
pct set 112 --cores 8 --memory 4096 --swap 1024 --onboot 1
pct set 112 --startup order=40,up=60,down=45
pct set 112 --mp0 /mnt/storage/game_backups,mp=/home/lgsm/backups
pct start 112

Install only game servers and files you are licensed to use. Test a server start, stop, update, backup, and restore before opening any port to the LAN.

Step 18: Build VM 113 - Docker Web Stack

Create VM 113 with 4 vCPUs, 4 GiB dedicated RAM, 32 GiB NVMe, fixed address 192.168.86.233/24, and vmbr0. Use a supported Debian or Ubuntu cloud image. Install Docker Engine and the Docker Compose plugin from the official Docker documentation. Do not install Docker inside every application LXC; keep this stack together in VM 113.

Inside VM 113:

sudo apt update
sudo apt full-upgrade -y
sudo apt install -y ca-certificates curl git cifs-utils nfs-common
# Install Docker Engine and the Compose plugin from the official Docker repository.
docker version
docker compose version
sudo mkdir -p /opt/stacks /mnt/shares/3d_assets /mnt/shares/backups

Mount the SMB or NFS shares using credentials stored in a root-only file. Test the mounts before starting Manyfold or Uptime Kuma. Store the stack files and .env files in /``mnt``/shares/backups/docker-stacks after testing.

The per-service bootstrap scripts are stored in storage/``project_scripts``/docker/. Copy the scripts to VM 113, review every image tag and environment value, make them executable, and run one service at a time:

chmod 700 /root/docker-scripts/*.sh
/root/docker-scripts/01-pihole-backup.sh
/root/docker-scripts/02-gravity-sync.sh
/root/docker-scripts/03-manyfold.sh
/root/docker-scripts/04-homebox.sh
/root/docker-scripts/05-netbox.sh
/root/docker-scripts/06-grocy.sh
/root/docker-scripts/07-mealie.sh
/root/docker-scripts/08-uptime-kuma.sh
/root/docker-scripts/09-watchtower.sh

Each script creates an isolated Compose project under /opt/stacks/<service>, writes a starter .env with generated placeholders, and starts only that service. Replace every placeholder secret before exposing the service. NetBox and Manyfold may need additional PostgreSQL or Redis configuration; the scripts deliberately keep those dependencies visible instead of silently creating shared production databases.

Step 19: Configure VM 113 Docker services

Set the backup instance to a different address or port strategy than the primary Pi-hole at 192.168.86.254. Restore a Teleporter archive and verify DNS resolution from a test client.

Set the primary and secondary Pi-hole addresses and a long random synchronization secret. Run a dry run or read-only comparison first. Confirm that the secondary never overwrites the primary unexpectedly.

Confirm /``mnt``/shares/3d_assets is mounted and writable by the container user. Configure its database and Redis endpoints after CT 101 is ready. Open only port 3232 on the LAN.

Configure the persistent volume and open port 7745 only on the trusted LAN. Create the initial administrator and export a backup.

Configure the database, Redis, allowed hosts, and secret key. Open port 8000 only to administrators. Create a database backup before importing inventory.

Use port 9192 externally and container port 80 internally. Set the timezone, create the administrator, and export the application backup.

Use port 9925 externally and container port 9000 internally. Configure the database according to the supported image defaults, create the administrator, and test a recipe export.

Use port 3001. Monitor Proxmox, CT 100, CT 101, VM 113, and each published application endpoint. Store exports under /``mnt``/shares/backups.

Use notification-only behavior for stateful services. Automatic updates are acceptable only for services whose data and upgrade behavior have been reviewed. Never let an automatic update replace a backup and restore test.

Step 20: Apply final LXC and VM configuration

On the Proxmox host, inspect every configuration and compare it to the architecture block:

for id in 100 101 102 103 104 105 106 107 108 109 110 112; do
  echo "===== CT $id ====="
  pct config "$id"
done
qm config 113

Confirm every CT has the intended cores, memory, swap, bridge, IP, mount points, and startup order. Confirm media mounts are read-only where specified. Confirm CT 101 is reachable only through the backend segment. Confirm VM 113 has the two required host mounts and the intended LAN address.

Do not use pct set to blindly overwrite a complete configuration. Change one property, inspect the result, and test the service:

pct set <CTID> --cores <N>
pct config <CTID>
pct restart <CTID>
pct exec <CTID> -- systemctl --failed

Step 21: Full validation and reboot test

Run host checks:

pveversion -v
pvesm status
pct list
qm list
systemctl --failed
iostat -xz 1 5
free -h

Run container checks:

for id in 100 101 102 103 104 105 106 107 108 109 110 112; do
  pct exec "$id" -- hostname
  pct exec "$id" -- ip -br addr
  pct exec "$id" -- df -hT
  pct exec "$id" -- free -h
done

Test each workflow: SMB file copy, PostgreSQL backup and restore, media playback and transcoding, audiobook playback, Nextcloud upload and sync, Immich photo import, Paperless document ingestion, Deluge VPN kill switch, game-server backup, and each VM 113 web endpoint.

Reboot the host during a maintenance window:

reboot

After reboot, verify that storage mounts are present before CT 100 starts, CT 101 starts before its dependent services, VM 113 mounts its shares, and all health monitors return to green.

Step 22: Backups and operating record

Create a Proxmox backup schedule to separate storage. Create nightly PostgreSQL logical dumps. Back up Docker Compose files, .env files with secrets protected, application exports, and the host configuration. Test a restore instead of only checking that a backup file exists.

Complete this record:

Item Value
Proxmox version
Host serial
System NVMe serial
Data disk serials
Backup target
Last PostgreSQL restore test
Last VM 113 restore test
Last full reboot test
Operator and date

Troubleshooting

A container cannot write to a bind mount. Check host ownership, ACLs, unprivileged UID/GID mappings, and the read-only flag. Do not make every CT privileged to hide a permissions problem.

A media server cannot transcode. Check /dev/``dri, device permissions, driver support, and the application log. Confirm CPU transcoding works before debugging Quick Sync.

PostgreSQL is slow. Keep active data and WAL on NVMe, inspect iostat, check memory pressure, and confirm that USB bulk storage is not being used for the database.

Docker services restart repeatedly. Run docker compose logs --tail=100, inspect the generated .env, verify mounted paths, and verify database and Redis connectivity. Do not enable restart loops while the root error is unknown.

A Community Script fails. Save the output, review the current script source and prerequisites, and repair or remove the partial CT deliberately. Do not rerun a destructive script repeatedly against the same broken container.