After years of paying for Google One storage and trusting Big Tech with my files, I finally moved everything to a self-hosted Nextcloud instance running on my Beelink mini PC earlier this year β and I genuinely wish I'd done it sooner. No more 15GB caps, no more wondering what Google indexes, and no monthly subscription eating into the homelab budget. If you're already running Proxmox VE and want real ownership of your cloud storage, documents, contacts, and calendar, this guide walks through the exact setup I use in production today.
I've been running this configuration for about six months now, syncing roughly 200GB of documents, project archives, and older photos across my laptop, iPad, and phone without a single data loss incident. This isn't a sanitized tutorial copied from the docs β these are the real commands I ran, including the parts that tripped me up on the first attempt.
Why LXC Instead of Docker for Nextcloud
Every time I post about self-hosting, someone asks why I use LXC instead of Docker. For Nextcloud specifically, there's a concrete answer: direct filesystem access. Nextcloud's data directory needs to be readable by the www-data user, and in a Docker setup you end up wrestling with volume permission mismatches that add real friction to debugging. In an LXC container, the filesystem behaves exactly like a traditional server β no bind mount magic, no UID remapping headaches.
There's also the backup story. When I take a Proxmox LXC snapshot with PBS, it captures the entire container state β OS, PHP config, database, and data directory β in one consistent operation. Reconstructing a Docker Nextcloud setup from a partial volume backup after a failure is an afternoon I don't want to spend. If you haven't already, my complete Proxmox LXC self-hosting guide covers the fundamentals before we dive into Nextcloud specifics here.
The other advantage is performance predictability. Nextcloud runs as a standard Apache + PHP-FPM service in the LXC, so performance tuning follows conventional server documentation exactly. No Docker networking layer in the path, no container-to-container latency for Redis connections.
What You'll Need Before Starting
My Nextcloud instance lives on my Beelink SER5 Mini PC (~$299) β Ryzen 5 5500U, 16GB RAM β which handles Nextcloud alongside eight other LXC containers without showing any strain. If you're building a dedicated homelab node, that SER5 is still my primary recommendation in that price bracket. For Nextcloud's RAM requirements, 2GB allocated to the LXC is the minimum, but 4GB is where it stops feeling sluggish once you add Redis and have a few sync clients connected simultaneously. I upgraded my host to 32GB total last quarter with a Crucial 32GB DDR4 SODIMM kit (~$59), which gave me headroom to give Nextcloud 4GB without starving other services.
Before you start, make sure you have:
- Proxmox VE 8.x running on your hardware
- A Debian 12 (Bookworm) LXC template downloaded in Proxmox
- A domain name with DNS pointing to your homelab (or a local Tailscale/Headscale domain β see my Headscale guide)
- Traefik configured as your reverse proxy for automatic TLS β I walk through that in my Traefik v3 setup guide
- At least 64GB of storage allocated for the LXC (the OS + application take about 15GB; the rest is your data headroom)
One hardware note I always mention: homelab hardware running a database deserves a UPS. Nextcloud's MariaDB database does not survive sudden power cuts gracefully β I've seen corrupted databases from exactly this scenario in a previous setup. An APC Pure Sine Wave UPS 1500VA (~$189) covers my entire rack and buys 15β20 minutes for a clean shutdown. Well worth it when you're storing files you actually care about.
Step 1: Create the Proxmox LXC Container
In the Proxmox web UI, click your node and select "Create CT". The resource allocations I use in production:
- Template: Debian 12 (bookworm)
- Disk: 64GB on your fastest local storage β NVMe if you have it
- CPU: 2 cores (Nextcloud is mostly I/O-bound, not CPU-bound)
- RAM: 4096 MB
- Swap: 512 MB
- Network: Static IP on your LAN (I use 192.168.1.x; DHCP reservation also works)
- Unprivileged container: Yes β leave this checked
Start the container and open the console. Before anything else, get updates out of the way:
apt update && apt upgrade -y
apt install -y curl wget gnupg2 ca-certificates lsb-release apt-transport-https
Step 2: Install PHP 8.3, Apache, and MariaDB
Nextcloud 30 recommends PHP 8.3, which isn't in Debian 12's default repos. I use the Sury repository β it's been rock-solid for a couple of years and is widely used in the self-hosting community:
curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg
echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list
apt update
Now install Apache2, PHP 8.3, and the full list of extensions Nextcloud requires. Missing even one of these will result in Nextcloud's admin panel flagging warnings:
apt install -y apache2 libapache2-mod-php8.3 php8.3 php8.3-cli php8.3-mysql php8.3-xml php8.3-mbstring php8.3-gd php8.3-curl php8.3-zip php8.3-intl php8.3-bcmath php8.3-gmp php8.3-imagick php8.3-redis php8.3-apcu mariadb-server redis-server
Enable the Apache modules Nextcloud depends on:
a2enmod rewrite headers env dir mime ssl
systemctl restart apache2
Secure MariaDB and create the Nextcloud database. Run mysql_secure_installation first, then:
mysql -u root -p
# Inside MariaDB:
CREATE DATABASE nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER 'nextcloud'@'localhost' IDENTIFIED BY 'StrongPasswordHere';
GRANT ALL PRIVILEGES ON nextcloud.* TO 'nextcloud'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Step 3: Download and Install Nextcloud
I always pull the latest stable tarball directly from Nextcloud rather than using a third-party package. The official releases include the complete application without any distro-level modifications:
cd /var/www/html
wget https://download.nextcloud.com/server/releases/latest.tar.bz2
tar -xjf latest.tar.bz2
rm latest.tar.bz2
chown -R www-data:www-data nextcloud/
chmod -R 755 nextcloud/
Create an Apache virtual host. Since Traefik handles TLS externally, the LXC runs plain HTTP internally β this keeps the Apache config simple:
cat > /etc/apache2/sites-available/nextcloud.conf << 'EOF'
<VirtualHost *:80>
DocumentRoot /var/www/html/nextcloud/
ServerName nextcloud.yourdomain.com
<Directory /var/www/html/nextcloud/>
Options +FollowSymlinks
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/nextcloud_error.log
CustomLog ${APACHE_LOG_DIR}/nextcloud_access.log combined
</VirtualHost>
EOF
a2ensite nextcloud.conf
a2dissite 000-default.conf
systemctl reload apache2
I use Nextcloud's CLI installer to skip the web UI setup entirely β it's faster and scriptable:
sudo -u www-data php /var/www/html/nextcloud/occ maintenance:install \
--database "mysql" \
--database-name "nextcloud" \
--database-user "nextcloud" \
--database-pass "StrongPasswordHere" \
--admin-user "admin" \
--admin-pass "YourAdminPassword" \
--data-dir "/var/www/html/nextcloud/data"
Step 4: Configure Redis and APCu for Caching
This is the step most guides skip, and it's the one that makes the biggest practical difference. Without a memory cache, Nextcloud hits the database for every lock check and session lookup. With Redis and APCu, the difference is dramatic β I measured page load times dropping from around 900ms to under 200ms on my Beelink after enabling this.
Edit the Nextcloud config file:
nano /var/www/html/nextcloud/config/config.php
Add these lines inside the config array, before the closing );:
'memcache.local' => '\OC\Memcache\APCu',
'memcache.distributed' => '\OC\Memcache\Redis',
'memcache.locking' => '\OC\Memcache\Redis',
'redis' => [
'host' => 'localhost',
'port' => 6379,
'timeout' => 0.0,
],
'trusted_proxies' => ['YOUR_TRAEFIK_LXC_IP'],
'overwriteprotocol' => 'https',
'overwrite.cli.url' => 'https://nextcloud.yourdomain.com',
The trusted_proxies and overwriteprotocol lines are non-optional when running behind Traefik. Without them, Nextcloud generates HTTP links internally, which breaks CalDAV/CardDAV sync, file sharing links, and the security headers check in the admin panel. This exact issue cost me two hours of debugging on my first Nextcloud install.
Step 5: Traefik Routing and HTTPS
My Traefik v3 guide covers the full setup, so here I'll just share the dynamic config file I use for Nextcloud. Save this as nextcloud.yml in your Traefik dynamic config directory:
http:
routers:
nextcloud:
rule: "Host(`nextcloud.yourdomain.com`)"
entryPoints:
- websecure
tls:
certResolver: letsencrypt
service: nextcloud
middlewares:
- nextcloud-headers
- nextcloud-dav-redirect
middlewares:
nextcloud-headers:
headers:
customResponseHeaders:
X-Frame-Options: "SAMEORIGIN"
X-Content-Type-Options: "nosniff"
Referrer-Policy: "no-referrer"
Strict-Transport-Security: "max-age=31536000; includeSubDomains"
nextcloud-dav-redirect:
redirectRegex:
regex: "https://(.*)/.well-known/(card|cal)dav"
replacement: "https://${1}/remote.php/dav/"
permanent: true
services:
nextcloud:
loadBalancer:
servers:
- url: "http://YOUR_NEXTCLOUD_LXC_IP:80"
The DAV redirect middleware is something many people miss. Without it, CalDAV (calendar) and CardDAV (contacts) sync fails silently on iOS and Android clients because they do a well-known discovery request first. This one line saves a lot of head-scratching when your Nextcloud calendar app can't find the server.
Step 6: PHP OPcache and Background Jobs
Nextcloud's admin panel runs a server health check and will flag misconfigured PHP settings loudly. Here are the values I set in /etc/php/8.3/apache2/php.ini:
memory_limit = 512M
upload_max_filesize = 16G
post_max_size = 16G
max_execution_time = 300
max_input_time = 300
opcache.enable = 1
opcache.memory_consumption = 128
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 10000
opcache.revalidate_freq = 1
opcache.save_comments = 1
Restart Apache after the change: systemctl restart apache2.
For background jobs, switch from the default AJAX mode to system cron. AJAX-based background jobs only run when someone is actively using Nextcloud, which means file indexing, activity cleanup, and notification delivery fall behind if you don't log in regularly. System cron runs reliably every five minutes regardless of activity:
crontab -u www-data -e
# Add:
*/5 * * * * php -f /var/www/html/nextcloud/cron.php
# Tell Nextcloud to use cron mode:
sudo -u www-data php /var/www/html/nextcloud/occ background:cron
Step 7: Backup With Proxmox Backup Server
Running your own cloud storage means you own the backup responsibility entirely. My strategy uses Proxmox Backup Server (PBS), which I've documented in my PBS homelab setup guide. I schedule a daily LXC snapshot at 2 AM with a retention policy of 7 daily + 4 weekly backups.
For database consistency, I wrap each backup with a maintenance mode toggle. Here's the hook script I attach to the PBS backup job:
#!/bin/bash
# pre-backup hook: enable maintenance mode
pct exec CTID -- sudo -u www-data php /var/www/html/nextcloud/occ maintenance:mode --on
# post-backup hook: disable maintenance mode
pct exec CTID -- sudo -u www-data php /var/www/html/nextcloud/occ maintenance:mode --off
I also run a separate nightly mysqldump that writes a compressed SQL file to a secondary location β the LXC snapshot gets the full filesystem state, but having a standalone database dump means I can restore just the data without spinning up an entire container. Belt and suspenders.
My Setup After Six Months in Production
Running Nextcloud on my Beelink for six months has changed how I work with files more than I expected. I now have a genuinely private cloud that's faster than Google Drive for files under 1GB (no round-trip to a remote data center), and I control the data retention policies entirely. I sync documents, my entire ~/Projects folder, and a shared folder with my wife for household documents β all encrypted in transit via Traefik's TLS, and backed up nightly.
The Nextcloud apps ecosystem also deserves a mention. The CalDAV/CardDAV integration works correctly with iOS and Android once the Traefik DAV redirect is in place β my contacts and calendar sync without issue. I enabled the Notes app for quick mobile-to-desktop note sync, which replaced my old Notion habit for ephemeral notes. The Nextcloud Office integration (Collabora Online) is on my list to set up next, which would turn this into a genuine Google Workspace replacement for document editing.
File sync performance on my LAN is fast enough that I don't feel any friction β gigabit internal network handles even large transfers quickly. If you're running 2.5GbE infrastructure (I added a TP-Link 2.5G 8-Port Switch (~$49) to my rack last year), bulk syncs of large media archives are noticeably faster. For most document and photo workflows, standard gigabit is plenty.
The total resource footprint on my Proxmox host is surprisingly light: the Nextcloud LXC uses about 600MB of RAM at idle (Redis + MariaDB + Apache), and CPU usage spikes only during file indexing or when multiple clients sync simultaneously. Even with nine LXC containers running, my Beelink's memory pressure stays comfortable.
Need help setting up your homelab?
I help individuals and small teams design and deploy Proxmox-based self-hosted infrastructure β from hardware selection to running services in production. Let's talk β