[{"data":1,"prerenderedAt":188},["ShallowReactive",2],{"seo-verification":3,"blog-2026-homelab-stack-vps-five-self-hosted-services-en":6},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"key":7,"data":8},"blog-2026-homelab-stack-vps-five-self-hosted-services-en",{"id":9,"slug":10,"slugs":11,"title":15,"excerpt":16,"readTime":17,"views":18,"isPinned":19,"publishedAt":20,"updatedAt":21,"category":22,"categories":27,"featuredImage":29,"bgImage":30,"posterImage":31,"relatedSolution":29,"intro":32,"sections":33,"ctaTitle":124,"ctaBody":125,"ctaButton":126,"ctaUrl":127,"relatedPosts":128},388,"2026-homelab-stack-vps-five-self-hosted-services",{"fr":12,"en":10,"ar":13,"es":14},"homelab-stack-vps-2026","stack-homelab-vps-2026-5-خدمات-self-hosted","stack-homelab-vps-2026-cinco-servicios-self-hosted","2026 Homelab Stack VPS: Five Self-Hosted Services","Deploy five self-hosted services — Nextcloud, Vaultwarden, Jellyfin, Immich and Paperless-ngx — in a unified Docker Compose with Caddy as reverse proxy on a single VPS.",10,0,false,"2026-09-29T00:00:00+00:00","2026-09-29T14:40:42+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":25},3,"Deployment","deploiement","bg-success\u002F10 text-success",[28],{"id":23,"name":24,"slug":25,"color":26,"icon":25},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Fhomelab-stack-vps-2026-poster.svg","Hosting one application on a VPS has become routine for developers. Hosting five applications at once — a file manager, a password vault, a media server, a photo library and a document archiver — on the same VPS, with a single composition file and a single SSL certificate, is another matter. This guide presents the complete architecture of the 2026 homelab stack: how to size the server, how to structure the `docker-compose.yml`, how to configure Caddy to route each subdomain, and how to avoid the most common startup errors.",[34,38,49,52,80,114,118,121],{"type":35,"title":36,"body":37},"h2","Why consolidate five services on a single VPS","The \"one app, one VPS\" approach has a logic: total isolation, independent deployment, no risk of contention. It also has a real cost — five servers, five IP addresses, five renewals, five nginx configurations, five TLS certificates to monitor. For a personal stack or a small team, this cost is only justified if the applications have very different peak loads or incompatible security requirements.\n\nNextcloud, Vaultwarden, Jellyfin, Immich and Paperless-ngx share the characteristic of being moderate-traffic applications, used primarily by one or a few users. Vaultwarden consumes less than 50 MB of RAM in idle mode. Paperless-ngx runs around 200 MB. Immich, the most resource-hungry at rest outside of transcoding, stays under 400 MB idle. These measurements are documented in the respective GitHub projects and in self-hosting community feedback.\n\nConsolidation on a VPS does not eliminate risks — it concentrates them. The trade-off is a single entry point to harden, a single certificate to manage, and a versioned Docker Compose manifest that constitutes the complete documentation of the infrastructure.",{"type":39,"title":40,"items":41},"ul","What this stack delivers in practice",[42,43,44,45,46,47,48],"**Data sovereignty** — files, passwords, photos and documents stay on your server, under your encryption key, with no dependency on a third-party cloud provider.","**A single wildcard TLS certificate** — Caddy automatically requests and renews `*.your-domain.com` via the DNS-01 challenge, covering all stack subdomains in a single configuration.","**Isolated internal Docker network** — no application exposes a port publicly; all inbound traffic passes through Caddy on 80\u002F443, and services communicate over a private bridge network.","**Named volumes and predictable restoration** — each service declares its data in a named Docker volume (`nextcloud_data`, `vaultwarden_data`…), making backups and migrations reproducible with a single `rsync` command.","**Independent updates** — pulling a new image for Immich does not restart Jellyfin or Paperless-ngx; `docker compose up -d --no-deps immich` only touches the relevant service.","**Fixed and predictable cost** — a fixed-resource VPS eliminates billing surprises generated by cloud services when Jellyfin transcoding spikes or Paperless-ngx OCR tasks accumulate.","**Possible service mesh** — Nextcloud can use Redis and MariaDB already present in the Compose; Immich shares the same network as the reverse proxy without additional configuration.",{"type":35,"title":50,"body":51},"Prerequisites: sizing and ports","The realistic floor for this stack in common use is **4 vCPU \u002F 8 GB RAM \u002F 100 GB SSD storage**. This sizing covers the idle footprints of each service and leaves margin for on-demand Jellyfin transcoding and Paperless-ngx OCR tasks, which are the two non-trivial load spikes of the stack.\n\nMeasured idle footprints (no active session, no background task):\n\n- **Nextcloud** (PHP-FPM + cron): ~300 MB depending on sync load.\n- **Vaultwarden**: \u003C 50 MB, very compact Rust image.\n- **Jellyfin**: ~250 MB without active transcoding. In soft transcoding (x264, 1080p): 1 to 2 vCPU at peak.\n- **Immich** (server + microservices): ~350-400 MB at rest. Machine learning tasks (face detection, classification) can consume up to 2 GB of RAM depending on volume.\n- **Paperless-ngx** (web + worker): ~200 MB. Tesseract OCR on a 50-page PDF can temporarily saturate a vCPU.\n- **Caddy**: \u003C 30 MB.\n- **Databases** (MariaDB for Nextcloud + Paperless, Redis): ~200 MB combined.\n\nEstimated total at rest: ~1.6 GB out of the 8 GB available. The margin absorbs peaks and allows adding another service without resizing.\n\nPorts to open on the firewall: **80\u002Ftcp** and **443\u002Ftcp** only. All other ports remain closed — internal services are not directly exposed.",{"type":53,"title":54,"steps":55},"steps","Step-by-step deployment",[56,59,62,65,68,71,74,77],{"title":57,"body":58},"Prepare the VPS","Connect via SSH to your VPS and update the system: `apt update && apt upgrade -y`. Install Docker and the Compose plugin: `curl -fsSL https:\u002F\u002Fget.docker.com | sh`. Verify the installation: `docker compose version`. Create a dedicated non-root user and add them to the `docker` group: `adduser deploy && usermod -aG docker deploy`. Enable UFW with minimal rules: `ufw allow 22\u002Ftcp && ufw allow 80\u002Ftcp && ufw allow 443\u002Ftcp && ufw enable`.",{"title":60,"body":61},"Configure DNS","In your DNS zone, create an A record for the root domain pointing to your VPS IP, then CNAME or A subdomains for each service: `nextcloud.your-domain.com`, `vault.your-domain.com`, `jellyfin.your-domain.com`, `photos.your-domain.com` and `docs.your-domain.com`. If you use Caddy's DNS-01 challenge for the wildcard certificate, ensure your DNS provider has an API supported by the corresponding `caddy-dns` module. Wait for DNS propagation (a few minutes to hours depending on the configured TTL).",{"title":63,"body":64},"Create the folder structure","Create the project tree on the VPS: `mkdir -p \u002Fopt\u002Fhomelab\u002F{caddy,nextcloud,vaultwarden,jellyfin,immich,paperless}`. This directory will contain the `docker-compose.yml` file, the `Caddyfile` and environment files. Persistent data will be stored in named Docker volumes, not bind-mounts, to simplify backups and avoid permission issues.",{"title":66,"body":67},"Write the Caddyfile","In `\u002Fopt\u002Fhomelab\u002Fcaddy\u002FCaddyfile`, declare one block per subdomain. Example for Nextcloud: `nextcloud.your-domain.com { reverse_proxy nextcloud:80 }`. Repeat the pattern for each service pointing to the Docker service name (`vaultwarden`, `jellyfin`, `immich-server`, `paperless-webserver`). Caddy obtains and renews Let's Encrypt certificates automatically on first access. For a wildcard certificate, replace individual blocks with `*.your-domain.com` with your provider's DNS module, configured via the environment variables of the custom Caddy image.",{"title":69,"body":70},"Write the docker-compose.yml","Create `\u002Fopt\u002Fhomelab\u002Fdocker-compose.yml` with a shared `proxy` network and an isolated `internal` network. Declare the Caddy service with `ports: [\"80:80\", \"443:443\"]` and `volumes: [\".\u002Fcaddy\u002FCaddyfile:\u002Fetc\u002Fcaddy\u002FCaddyfile\", \"caddy_data:\u002Fdata\"]`. For each application, declare `networks: [proxy, internal]` and expose **no** `ports:` — only Caddy exposes public ports. Use `depends_on` with `condition: service_healthy` so Nextcloud does not try to join MariaDB before it is ready. Sensitive variables (database passwords, secret keys) go in a `.env` file referenced by `env_file: .env`.",{"title":72,"body":73},"Configure environment variables","Create `\u002Fopt\u002Fhomelab\u002F.env` with variables required by each service: `MYSQL_ROOT_PASSWORD`, `MYSQL_DATABASE`, `MYSQL_USER`, `MYSQL_PASSWORD` for MariaDB; `NEXTCLOUD_ADMIN_USER`, `NEXTCLOUD_ADMIN_PASSWORD`, `NEXTCLOUD_TRUSTED_DOMAINS` for Nextcloud; `VAULTWARDEN_ADMIN_TOKEN` for Vaultwarden. Generate secrets with `openssl rand -hex 32`. For Immich, copy the example `.env` file from the official repository — it declares required variables and their defaults. Never commit this file to a public repository; add `.env` to your `.gitignore`.",{"title":75,"body":76},"Launch the stack","From `\u002Fopt\u002Fhomelab`, run `docker compose pull` to download all images, then `docker compose up -d` to start everything. Follow startup logs with `docker compose logs -f` to ensure each service starts without error. The first initialization of Nextcloud and Paperless-ngx may take a few minutes (database migrations, key generation). Caddy obtains TLS certificates on first access to each subdomain — verify that ports 80 and 443 are accessible from outside before testing.",{"title":78,"body":79},"Finalize configuration for each service","Access each web interface to complete initial configuration: Nextcloud (`nextcloud.your-domain.com`) to enable recommended apps (Contacts, Calendar, Talk); Immich (`photos.your-domain.com`) to configure libraries and enable background machine learning tasks; Paperless-ngx (`docs.your-domain.com`) to configure the document consumer and OCR; Jellyfin (`jellyfin.your-domain.com`) to point to media folders mounted as volumes. Vaultwarden (`vault.your-domain.com`) only requires account creation from the Bitwarden client — no initial server configuration needed.",{"type":81,"title":82,"headers":83,"rows":88},"comparison","Caddy, Nginx Proxy Manager or Traefik: which reverse proxy for this stack",[84,85,86,87],"Criterion","Caddy","Nginx Proxy Manager","Traefik",[89,94,99,104,109],[90,91,92,93],"Configuration","Declarative Caddyfile, reload without downtime","Web interface, no files to edit","Docker labels, automatic hot-reload",[95,96,97,98],"Automatic SSL","Built-in, DNS-01 and HTTP-01 native","Let's Encrypt via interface, DNS-01 possible","Let's Encrypt via ACME resolver, DNS-01 via providers",[100,101,102,103],"Wildcard","Native via caddy-dns module","Possible but requires manual setup","Native via certificateResolvers",[105,106,107,108],"Learning curve","Low — Caddyfile readable in 10 minutes","Very low — everything is done by clicking","Medium — voluminous documentation",[110,111,112,113],"Suited to this stack","Yes — versioned config, live reload","Yes for getting started, less ideal for versioning","Yes for more complex stacks, config overhead here",{"type":115,"title":116,"body":117},"tip","Minimal hardening before public exposure","Four measures to apply before making the stack accessible from outside:\n\n1. Disable Vaultwarden's open registration page by setting `SIGNUPS_ALLOWED=false` in the `.env` once your account is created.\n2. Add an `X-Robots-Tag: noindex` header in the Caddyfile for Vaultwarden and the Paperless-ngx admin interface — these pages should not be indexed.\n3. Enable Docker log rotation (`log-opts` in `\u002Fetc\u002Fdocker\u002Fdaemon.json`) to prevent Jellyfin or Paperless-ngx logs from filling the disk.\n4. Schedule a weekly `docker compose pull && docker compose up -d` via cron to keep images up to date — self-hosted applications regularly publish security patches. Check changelogs before updating Immich, which may introduce non-reversible database migrations.",{"type":35,"title":119,"body":120},"Troubleshooting: common startup errors","**`Error response from daemon: network proxy declared as external, but could not be found`** — This message appears when the external Docker network declared in `docker-compose.yml` does not yet exist. Create it manually before the first `docker compose up`: `docker network create proxy`. Or switch the network to internal in the Compose (remove `external: true`) so Compose creates it itself.\n\n**`nextcloud.your-domain.com redirected you too many times`** — Nextcloud detects the HTTPS request as HTTP because Caddy forwards it in HTTP over the internal network. Add `NEXTCLOUD_TRUSTED_PROXIES` with the Docker subnet (e.g. `172.16.0.0\u002F12`) and `OVERWRITEPROTOCOL=https` in the `.env`. Without these variables, Nextcloud does not trust the `X-Forwarded-Proto` headers transmitted by Caddy and attempts to redirect to HTTPS indefinitely.\n\n**`Immich cannot connect to database: connection refused`** — Immich starts before PostgreSQL is ready. Add `depends_on` with `condition: service_healthy` on the `immich-server` service and verify that the `database` service declares a valid `healthcheck` (e.g. `pg_isready -U immich`). Without a healthcheck, Docker Compose considers the service \"started\" as soon as the container launches, not when it accepts connections.\n\n**`Paperless-ngx worker exited with error: celery worker unhealthy`** — The Redis database is not accessible. Check that the Redis service is on the same network as Paperless and that the `PAPERLESS_REDIS` variable points to the Compose service name (`redis:\u002F\u002Fredis:6379`), not `localhost` — in Docker Compose, `localhost` inside a container points to the container itself, not to another service.\n\n**Caddy does not renew the wildcard certificate** — If you use the DNS-01 challenge, verify that the DNS API environment variables (token, zone ID) are properly passed to the Caddy service in Compose. An expired token or insufficient DNS permission causes the renewal to fail silently 30 days before expiration — Caddy logs the error but does not block traffic until actual expiration.",{"type":35,"title":122,"body":123},"Going further","This stack covers the five most requested services in 2026 homelab configurations. Each has a dedicated guide on this blog if you want to go deeper on a specific aspect: Nextcloud backup, Immich album management, Jellyfin hardware transcoding, Paperless-ngx retention rules or Vaultwarden multi-device sync.\n\nBeyond this stack, the next components commonly added are Uptime Kuma (internal service monitoring) and Ntfy or Apprise (push notifications). These services are lightweight enough to be added to the same Compose without impacting sizing.\n\nIf manual update and backup management becomes a burden, Coolify and Dokploy offer interfaces that automate these tasks while preserving the underlying Docker Compose architecture — see the Heroku\u002FVercel to VPS migration guide for context.","A VPS with root access for your homelab stack","ServOrbit.com offers cloud VPS plans from 99 DH\u002Fmonth, with dedicated IPv4, choice of OS (Ubuntu, Debian, AlmaLinux), high-availability network and snapshots. Deploy this entire stack with a single `docker compose up -d` command.","Deploy on VPS","\u002Fvps-cloud",[129,153,170],{"id":130,"slug":131,"slugs":132,"title":136,"excerpt":137,"readTime":138,"views":139,"isPinned":19,"publishedAt":140,"updatedAt":141,"category":142,"categories":148,"featuredImage":29,"bgImage":30,"posterImage":150,"relatedSolution":151},82,"host-immich-on-your-own-vps",{"fr":133,"en":131,"ar":134,"es":135},"heberger-immich","استضافة-immich-على-خادمك-الافتراضي-الخاص-vps","alojar-immich-en-un-vps","Host Immich on Your Own VPS","Host Immich on your VPS: a self-hosted alternative to Google Photos with mobile backup, facial recognition and SSL via Docker. Includes v3 migration guide.",11,1,"2026-03-30T00:00:00+00:00","2026-09-23T15:23:17+00:00",{"id":143,"name":144,"slug":145,"color":146,"icon":147},7,"Self-hosting","self-hosting","bg-indigo-500\u002F10 text-indigo-400","cloud",[149],{"id":143,"name":144,"slug":145,"color":146,"icon":147},"\u002Fblog\u002Fcovers\u002Fheberger-immich-poster.svg",{"categorySlug":145,"appSlug":152},"immich",{"id":154,"slug":155,"slugs":156,"title":160,"excerpt":161,"readTime":162,"views":18,"isPinned":19,"publishedAt":163,"updatedAt":141,"category":164,"categories":165,"featuredImage":29,"bgImage":30,"posterImage":167,"relatedSolution":168},71,"hosting-nextcloud-on-your-own-vps",{"fr":157,"en":155,"ar":158,"es":159},"heberger-nextcloud","استضافة-nextcloud-على-خادمك-الـvps-الخاص","alojar-nextcloud-en-su-propio-vps","Self-Host Nextcloud on VPS: Complete Guide, NC 34 and Security","Deploy Nextcloud 34.0.2 on your VPS with Docker and Redis. Complete guide: missing NC 34.0.0 apps, PostgreSQL oc_filecache_extended constraint, and 2FA CVEs.",14,"2026-04-10T00:00:00+00:00",{"id":143,"name":144,"slug":145,"color":146,"icon":147},[166],{"id":143,"name":144,"slug":145,"color":146,"icon":147},"\u002Fblog\u002Fcovers\u002Fheberger-nextcloud-poster.svg",{"categorySlug":145,"appSlug":169},"nextcloud",{"id":171,"slug":172,"slugs":173,"title":177,"excerpt":178,"readTime":17,"views":23,"isPinned":19,"publishedAt":179,"updatedAt":180,"category":181,"categories":182,"featuredImage":29,"bgImage":30,"posterImage":184,"relatedSolution":185},84,"host-jellyfin-on-your-own-vps",{"fr":174,"en":172,"ar":175,"es":176},"heberger-jellyfin","استضافة-jellyfin-على-خادمك-الافتراضي-الخاص-vps","alojar-jellyfin-en-un-vps","Self-Hosting Jellyfin on a VPS: Complete 2026 Guide","Deploy Jellyfin on your own VPS with Docker, nginx, HTTPS, plugins, storage options and hardware transcoding. Full guide updated for 2026.","2026-03-28T00:00:00+00:00","2026-09-07T11:26:10+00:00",{"id":143,"name":144,"slug":145,"color":146,"icon":147},[183],{"id":143,"name":144,"slug":145,"color":146,"icon":147},"\u002Fblog\u002Fcovers\u002Fheberger-jellyfin-poster.svg",{"categorySlug":186,"appSlug":187},"collaboration-productivity","jellyfin",1790693239114]