# 114 β€” Koillection Deployment Guide > Status: **IN PROGRESS** β€” CT created 2026-08-11, base setup + Docker install done, NAS mount pending (Phase 4+) > CT ID: 114 Β· IP: 192.168.1.114 > Domain: `collections.spendlik.sk` > Last updated: 2026-08-11 --- ## Overview Koillection is a self-hosted collection manager for tracking physical collections of any kind. No pre-built metadata scrapers β€” metadata is added freely per item, with custom fields via templates. MIT licensed. **Collections planned for this instance:** - πŸš— Hot Wheels (series, year, colour, condition, variants) - 🧱 LEGO (set number, theme, piece count, minifigures, completion status) - πŸ¦‡ Batmobiles (source media, scale, manufacturer, condition) - πŸ“š Comics (title, issue, publisher, language, condition) - πŸ“„ Paper Models (designer, scale, subject, format β€” physical printed copies) Stack: `koillection/koillection` (PHP/Symfony + Vue.js) + PostgreSQL 16. Uploads (item photos) bind-mounted to NAS for data safety. --- ## Resource Allocation | Resource | Allocation | |---|---| | **CT ID** | 114 | | **IP** | 192.168.1.114 | | **CPUs** | 1 | | **RAM** | 512 MB | | **Disk** | 8 GB (app + DB only; photos on NAS) | | **Template** | Debian 13 (trixie) β€” created from `13.1-2` (current template at time of creation; `13.6-1` is now the default for new deployments, but this doesn't matter post-creation β€” see `Proxmox LXC Templates.md`) | | **Privileged** | Yes (Docker requires it) | | **Nesting** | Enabled (`features: nesting=1`) | --- ## NAS Mount Planning Photos uploaded to Koillection land in `/uploads` inside the container. This will be bind-mounted from the NAS at: ``` /volume1/proxmox/data/koillection/uploads ``` Create this directory on the NAS before deployment: ```bash # On the Synology NAS (SSH or File Station) mkdir -p /volume1/proxmox/data/koillection/uploads ``` > ⚠️ The NAS path follows the same convention as Paperless (`/volume1/proxmox/data/`). Be consistent. > > **Host-side mount**: verified 2026-08-11 β€” the Proxmox storage ID is `spendlik-nas`, mounted at `/mnt/pve/spendlik-nas` on the host (confirmed via live `ls /mnt/pve/`). CT 111 (Paperless) uses `/mnt/pve/spendlik-nas/data/paperless` as its exact host-side bind-mount path β€” Koillection follows the identical pattern in Phase 4 below. --- ## Phase 1 β€” Create LXC Container βœ… DONE (2026-08-11) ```bash pct create 114 local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst \ --hostname koillection \ --cores 1 \ --memory 512 \ --swap 512 \ --rootfs local-lvm:8 \ --net0 name=eth0,bridge=vmbr0,ip=192.168.1.114/24,gw=192.168.1.1 \ --unprivileged 0 \ --features nesting=1 \ --ostype debian \ --start 1 ``` Enter the container: ```bash pct enter 114 ``` --- ## Phase 2 β€” Base Setup βœ… DONE (2026-08-11) ```bash apt update && apt upgrade -y apt install -y nano curl ca-certificates gnupg lsb-release ``` --- ## Phase 3 β€” Install Docker βœ… DONE (2026-08-11) ```bash install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg chmod a+r /etc/apt/keyrings/docker.gpg echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/debian \ $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null apt update apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin ``` Verify: ```bash docker run --rm hello-world ``` --- ## Phase 4 β€” NAS Bind Mount Add the NAS uploads path as a Proxmox bind mount. Exit the container first: ```bash exit ``` On the Proxmox host: ```bash pct set 114 --mp0 /mnt/pve/spendlik-nas/data/koillection/uploads,mp=/uploads,shared=1 ``` > βœ… Verified 2026-08-11 against live `ls /mnt/pve/` (only `spendlik-nas` present) and cross-checked against CT 111 (Paperless)'s actual documented host mount (`/mnt/pve/spendlik-nas/data/paperless`) β€” this replaces an earlier placeholder path in this guide that would have failed (wrong storage name). > > ⚠️ Make sure `/volume1/proxmox/data/koillection/uploads` exists on the NAS first (see "NAS Mount Planning" above) before running this β€” if it hasn't been created yet, do that via NAS SSH/File Station first. Re-enter the container and verify the mount is visible: ```bash pct enter 114 ls /uploads ``` --- ## Phase 5 β€” Deploy Koillection ```bash mkdir -p /opt/koillection cd /opt/koillection nano .env ``` Paste (fill in a strong password for `DB_PASSWORD`): ```env DB_DRIVER=pdo_pgsql DB_NAME=koillection DB_HOST=db DB_PORT=5432 DB_USER=koillection DB_PASSWORD=CHANGE_ME DB_VERSION=16 APP_ENV=prod APP_DEBUG=0 APP_SECRET=CHANGE_ME_32CHAR_RANDOM_STRING PHP_TZ=Europe/Bratislava HTTPS_ENABLED=0 ``` > Generate `APP_SECRET` with: `openssl rand -hex 16` ```bash nano docker-compose.yml ``` Paste: ```yaml services: koillection: image: koillection/koillection:latest container_name: koillection restart: unless-stopped ports: - "8080:80" env_file: - .env volumes: - /uploads:/uploads depends_on: db: condition: service_healthy db: image: postgres:16 container_name: koillection-db restart: unless-stopped env_file: - .env environment: - POSTGRES_DB=${DB_NAME} - POSTGRES_USER=${DB_USER} - POSTGRES_PASSWORD=${DB_PASSWORD} volumes: - ./volumes/postgresql:/var/lib/postgresql/data healthcheck: test: ["CMD", "pg_isready", "-q", "-d", "koillection", "-U", "koillection"] timeout: 45s interval: 10s retries: 10 ``` Start: ```bash docker compose up -d docker compose logs -f ``` Wait until the koillection container logs settle (Symfony app startup). Then verify locally: ```bash curl -s http://localhost:8080 | grep -i koillection ``` --- ## Phase 6 β€” nginx Reverse Proxy (CT 101) Enter CT 101: ```bash pct enter 101 nano /etc/nginx/sites-available/koillection ``` Paste: ```nginx server { listen 80; server_name collections.spendlik.sk; location / { proxy_pass http://192.168.1.114:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; client_max_body_size 20M; } } ``` > `client_max_body_size 20M` β€” item photos can be large. Adjust upward if needed. Enable and reload: ```bash ln -s /etc/nginx/sites-available/koillection /etc/nginx/sites-enabled/ nginx -t && systemctl reload nginx ``` --- ## Phase 7 β€” SSL Certificate Still in CT 101: ```bash certbot --nginx -d collections.spendlik.sk ``` > ⚠️ Always inspect the config after certbot: ```bash cat /etc/nginx/sites-available/koillection ``` Check for duplicate `server_name` directives and missing closing braces. Fix manually if needed. Also set `HTTPS_ENABLED=1` in `/opt/koillection/.env` in CT 114, then restart: ```bash # In CT 114 cd /opt/koillection nano .env # set HTTPS_ENABLED=1 docker compose restart koillection ``` --- ## Phase 8 β€” DNS Record > ℹ️ **Updated 2026-08-10**: All `*.spendlik.sk` subdomains are now **CNAME** records pointing at the root `spendlik.sk`. Only the root `spendlik.sk` A record holds an IP β€” WebSupport rejects any duplicate IP value elsewhere in the zone. Do **not** create an A record for this subdomain. In WebSupport admin panel: 1. Add **CNAME** record: `collections` β†’ `spendlik.sk` 2. Check both DNS management pages 3. Note the numeric record ID 4. Add to `00_index.md` DNS table > βœ… No DDNS updater step is needed for this subdomain. `ddns-update.sh` on CT 108 only updates the root A record on IP change; this CNAME resolves through automatically. --- ## Phase 9 β€” Authelia Protection (CT 102) Enter CT 102, edit `/etc/authelia/configuration.yml`. Add to `access_control.rules`: ```yaml - domain: collections.spendlik.sk policy: two_factor ``` > Koillection has its own internal login system. Authelia adds a second layer before users even reach the login page. Since this is a personal single-user instance, you may prefer Authelia bypass and rely on Koillection's own login instead β€” your call. Restart Authelia after editing: ```bash docker compose restart ``` Add the Authelia middleware to the nginx vhost in CT 101 (follow the pattern from other protected services). --- ## Phase 10 β€” First Login & Initial Setup Open `https://collections.spendlik.sk` from mobile data (hairpin NAT β€” never test from LAN). On first load, Koillection will prompt you to create an admin account. Do so, then: 1. Set your timezone to `Europe/Bratislava` in profile settings 2. Set the display currency if tracking purchase values 3. Set visibility defaults (private by default is fine for a personal instance) --- ## Phase 11 β€” Collection Setup Recommended collection structure. Create each as a top-level Collection: ### πŸš— Hot Wheels Suggested item fields (via Template): - Series / Line - Year of release - Colour - Casting name - Country of manufacture - Condition (Mint / Good / Played) - Treasure Hunt (yes/no) ### 🧱 LEGO Suggested item fields: - Set number - Theme - Sub-theme - Piece count - Minifigure count - Year - Completion status (Sealed / Built / Parts only) - Instruction booklet present (yes/no) > πŸ’‘ The set number field makes cross-referencing with kocka-novinky.sk and Brickset API straightforward. ### πŸ¦‡ Batmobiles Suggested item fields: - Source (Film / TV / Comics / Game) - Year of appearance - Manufacturer (Hot Wheels / Corgi / LEGO / custom) - Scale - Condition ### πŸ“š Comics Suggested item fields: - Title / Series - Issue number - Publisher - Language - Year - Condition (Mint / Very Good / Good / Fair) - Story arc ### πŸ“„ Paper Models (physical hardcopy) Suggested item fields: - Designer / Publisher - Subject (aircraft, ship, building…) - Scale - Format (magazine supplement / standalone / kit) - Build status (Unbuilt / Built / Display) > πŸ’‘ Tags are cross-collection in Koillection β€” tag items with `#display`, `#wishlist`, `#for-sale` etc. to group across all five collections at once. --- ## Backup The only things that need backing up: 1. **PostgreSQL database** β€” contains all collection metadata 2. **NAS uploads directory** β€” contains all item photos (already on NAS, covered by NAS backup) Add a daily DB dump to cron in CT 114: ```bash crontab -e ``` Add: ```cron 0 3 * * * docker exec koillection-db pg_dump -U koillection koillection > /opt/koillection/backups/koillection-$(date +\%Y\%m\%d).sql 2>/dev/null ``` ```bash mkdir -p /opt/koillection/backups ``` > ⚠️ Always back up the database before upgrading Koillection β€” the developer notes that data migrations can occasionally have edge cases. --- ## Gotchas | Issue | Fix | |---|---| | Photos not saving | Verify `/uploads` bind mount is writable inside the container | | App not starting | Check `docker compose logs koillection` β€” usually a DB connection issue on first boot | | certbot corrupts nginx config | Always inspect after issuance | | Large photo uploads rejected | Increase `client_max_body_size` in nginx vhost | | HTTPS redirect loop | Set `HTTPS_ENABLED=1` in `.env` and restart the koillection container after SSL is in place | | DNS record type | Use CNAME β†’ `spendlik.sk`, never a per-subdomain A record (see Phase 8) | | Wrong template filename | Verify exact template string with `pveam list local` before `pct create` β€” versions bump periodically | | Wrong NAS host mount path | Storage ID is `spendlik-nas`, mounted at `/mnt/pve/spendlik-nas` β€” verify with `ls /mnt/pve/` before trusting any guide's hardcoded path |