Almost every homelab eventually goes through the same phase: you've got a Proxmox VE box humming away in the corner, a handful of VMs running things like Pi-hole or Jellyfin, and then one day you think "I should just host my own blog instead of paying for one." WordPress still powers a huge share of the websites on the internet, and running it yourself in a Proxmox VE LXC container is cheap, fast, and a genuinely useful skill to have.
This guide builds a WordPress site from a bare Debian container — no Docker, no one-click appliance, no bundled installer. Just Apache, MariaDB, PHP, and the WordPress files themselves, installed the same way a lot of small production sites are actually set up. It takes about 30 to 40 minutes if you're following along command by command.
What You Will Learn
By the end of this tutorial you'll have a working WordPress installation running inside an LXC container on your Proxmox VE host, reachable from a browser on your network. Along the way you'll pick up:
- How to create a properly sized LXC container for a web application
- How to install and wire together Apache, MariaDB, and PHP on Debian 12
- How to configure WordPress's database connection and secret keys by hand
- Why permalinks break by default, and the one setting that fixes it
- The handful of errors almost every new WordPress admin runs into, and how to read the actual log line that explains them
What Is This Feature?
WordPress is a content management system — software that lets you write, edit, and publish pages and blog posts through a web browser, without hand-coding HTML. It started as blogging software back in 2003 and has since grown into something people use for everything from personal blogs to full online stores. It's written in PHP and stores its content in a MySQL or MariaDB database.
An LXC container, in case you haven't run into the term yet, is Proxmox VE's lightweight alternative to a full virtual machine. Rather than emulating an entire PC the way a VM does, a container shares the host's Linux kernel and only isolates the filesystem and running processes. That means it starts in under a second, uses a fraction of the memory a comparable VM would need, and is a good fit for a single-purpose service like a web server.
MariaDB is the database WordPress will store its posts, pages, users, and settings in. It's a drop-in replacement for MySQL — same commands, same client tools, and it's what Debian installs by default when you ask for "mysql-server" style packages these days.
Why Would You Use It?
Hosted WordPress plans and website builders charge monthly, cap your storage, and often lock features behind higher tiers. Running WordPress yourself costs nothing beyond the electricity your Proxmox VE host is already using, and you're limited by your own disk, not somebody else's pricing table.
It's also just a solid way to learn how a real web stack fits together. A lot of "modern" hosting is Docker containers and managed platforms that hide what's actually happening underneath. Installing Apache, MariaDB, and PHP by hand on a plain Debian container shows you exactly what WordPress needs to run, which makes troubleshooting any web app — not just WordPress — much less mysterious later on.
And because it's an LXC container, spinning up a second one to test a theme change or a risky plugin update takes about a minute. Break it, delete it, try again.
Prerequisites
Before you start, make sure you have:
- A working Proxmox VE host, version 8.x or 9.x (the steps below are identical on both)
- At least 8 GB of free storage and 1 GB of free RAM to dedicate to the container
- A Debian 12 (bookworm) container template downloaded on your Proxmox VE node
- Basic comfort typing commands into a terminal — you don't need to be a Linux expert, just willing to type carefully
- Network access from your Proxmox VE host to the internet, so the container can download packages and WordPress itself
If you haven't downloaded a container template before: go to your node in the Proxmox VE web interface, click local (your-node-name) in the left-hand tree, open the CT Templates tab, click Templates, and download debian-12-standard. It's roughly 130 MB and takes a minute or two.
Step-by-Step Tutorial
1. Create the LXC container
In the Proxmox VE web interface, click Create CT in the top-right corner. Work through the wizard with these settings:
- Hostname: wordpress
- Unprivileged container: leave this checked — there's no reason a blog needs root-level access to the host
- Password: set a strong root password for the container itself
- Template: debian-12-standard
- Disk size: 16 GB (8 GB works for a bare install, but media uploads add up fast)
- CPU cores: 2
- Memory: 1024 MB, with 512 MB swap
- Network: bridge vmbr0, IPv4 via DHCP (or a static address if you've already set one aside)
Click Finish, then start the container and open its console.
2. Update the system and install the LAMP stack
First, bring the container fully up to date:
apt update && apt full-upgrade -y
Then install Apache, MariaDB, PHP, and the PHP extensions WordPress actually needs:
apt install -y apache2 mariadb-server php php-mysql php-gd php-xml \
php-mbstring php-curl php-zip libapache2-mod-php unzip wget
On a fresh Debian 12 container this pulls in PHP 8.2 and MariaDB 10.11 — both are current enough for the latest WordPress release. The install takes a couple of minutes.
3. Secure MariaDB and create the WordPress database
Run the built-in hardening script and answer its prompts (set a root password, remove anonymous users, disable remote root login, remove the test database):
mysql_secure_installation
Now log into MariaDB and create a dedicated database and user for WordPress. Never point WordPress at the MariaDB root account — a compromised plugin shouldn't be able to touch anything outside its own database.
mysql -u root -p
CREATE DATABASE wordpress;
CREATE USER 'wpuser'@'localhost' IDENTIFIED BY 'ReplaceThisWithAStrongPassword';
GRANT ALL PRIVILEGES ON wordpress.* TO 'wpuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;
4. Download and place the WordPress files
cd /tmp
wget https://wordpress.org/latest.tar.gz
tar xzf latest.tar.gz
cp -r wordpress/* /var/www/html/
chown -R www-data:www-data /var/www/html
find /var/www/html -type d -exec chmod 755 {} \;
find /var/www/html -type f -exec chmod 644 {} \;
That last pair of commands sets directories to 755 and files to 644 — the standard permission set that lets Apache read everything and WordPress write to its own upload folders, without leaving files writable by anyone who isn't the web server user.
5. Configure wp-config.php
cd /var/www/html
cp wp-config-sample.php wp-config.php
nano wp-config.php
Find these three lines and fill in the database details from step 3:
define( 'DB_NAME', 'wordpress' );
define( 'DB_USER', 'wpuser' );
define( 'DB_PASSWORD', 'ReplaceThisWithAStrongPassword' );
While you're in there, replace the block of AUTH_KEY, SECURE_AUTH_KEY, and similar placeholder lines with real random values. WordPress publishes a generator for exactly this at https://api.wordpress.org/secret-key/1.1/salt/ — open that URL, copy the output, and paste it over the matching lines in wp-config.php. These keys are what encrypt your login cookies; leaving the placeholder text in place is a real, if minor, security weakness.
6. Enable URL rewriting and finish the Apache config
WordPress needs Apache's rewrite module for "pretty" URLs like /2026/07/my-post/ instead of ?p=123:
a2enmod rewrite
Then edit /etc/apache2/apache2.conf and find the block that starts with <Directory /var/www/>. Change AllowOverride None to AllowOverride All so WordPress's own .htaccess file actually takes effect. Save, then restart Apache:
systemctl restart apache2
7. Run the WordPress setup wizard
Find the container's IP address (either from the Proxmox VE web UI summary tab, or by running ip addr show eth0 inside the container), then open http://<container-ip>/ in a browser. WordPress will walk you through picking a site title, an admin username (avoid "admin" — it's the first thing brute-force bots try), a strong password, and your email address.
Once that finishes, log in at http://<container-ip>/wp-admin and go to Settings > Permalinks. Pick Post name and save. This is the setting that actually exercises the rewrite rules you enabled in step 6 — if it saves without an error, your URL structure is working.
Commands Explained
| Command | What it does |
|---|---|
apt full-upgrade -y | Updates every installed package, including ones that need to remove or replace others — a more thorough version of a regular upgrade, good practice right after deploying a fresh container. |
mysql_secure_installation | An interactive script that walks a fresh MariaDB or MySQL install through basic hardening: setting a root password, dropping the anonymous test account, and disabling remote root logins. |
chown -R www-data:www-data /var/www/html | Makes the Apache user (www-data) the owner of every WordPress file, so the web server can read the site and write to folders like wp-content/uploads. |
a2enmod rewrite | Enables Apache's mod_rewrite module, which WordPress relies on for clean, human-readable URLs instead of query-string links. |
systemctl restart apache2 | Restarts the Apache web server so any configuration changes — like the AllowOverride setting or a newly enabled module — actually take effect. |
Common Errors
"Error establishing a database connection" — this is almost always a typo in wp-config.php, or MariaDB isn't running. Double-check DB_NAME, DB_USER, and DB_PASSWORD match exactly what you created in step 3, and confirm the database is up with systemctl status mariadb.
A blank white page with nothing on it — sysadmins call this the white screen of death, and it's almost always a PHP error that WordPress is hiding by default. Check /var/log/apache2/error.log for the actual message. A theme or plugin hitting PHP's memory limit is a common cause; you can raise it by adding define('WP_MEMORY_LIMIT', '256M'); near the top of wp-config.php.
404 errors on every page except the homepage — this means the permalink rewrite isn't working. Go back and confirm you ran a2enmod rewrite, changed AllowOverride to All, and restarted Apache. It's easy to fix the module but forget the AllowOverride line, or vice versa.
"Briefly unavailable for scheduled maintenance" stuck on screen — this shows up if a WordPress auto-update gets interrupted partway through. Delete the leftover marker file with rm /var/www/html/.maintenance and the site comes right back.
Troubleshooting
If something isn't working and the error messages above don't match what you're seeing, work through these in order:
- Check that Apache is actually running:
systemctl status apache2. If it's not,systemctl start apache2and look at the output for a config syntax error. - Check MariaDB the same way:
systemctl status mariadb. - Tail the Apache error log while you reload the page in your browser:
tail -f /var/log/apache2/error.log. Whatever PHP is choking on will usually show up here in plain English. - Confirm the container actually has internet access if a package install or the WordPress download fails:
ping -c 3 wordpress.org. If that fails, check the container's network config withip addr show eth0and the bridge assignment in the Proxmox VE UI. - If login to MariaDB is refused, test the credentials directly with
mysql -u wpuser -p wordpressoutside of WordPress — if that fails too, the problem is the database account, not WordPress.
Best Practices
Take a Proxmox VE snapshot before any major WordPress core, theme, or plugin update. Rolling back an LXC container snapshot takes seconds, and it's a far less stressful way to recover from a bad update than trying to hand-fix a broken site at midnight.
Keep WordPress core, themes, and plugins updated. Most real-world WordPress hacks target known vulnerabilities in outdated plugins, not some clever zero-day. This one habit prevents more incidents than any hardening plugin will.
Change the default database table prefix (wp_) to something random when you first set up wp-config.php, using the $table_prefix variable. It won't stop a determined attacker, but it does cut down on the constant background noise from automated bots that assume every WordPress site uses the default prefix.
If you plan to expose this site to the public internet, put it behind a reverse proxy with a Let's Encrypt certificate rather than forwarding port 80 straight to the container. It's also worth adding define('DISALLOW_FILE_EDIT', true); to wp-config.php, which removes the theme and plugin editor from wp-admin — a feature that's convenient but is also exactly what an attacker with a stolen admin password would use first.
Back up more than just the container. A WordPress site is really three things: the files in /var/www/html, the database, and (if you're using one) uploaded media. A vzdump backup of the whole container covers all three at once, but exporting the database separately with mysqldump every so often gives you a lighter, faster restore option if you only need to recover content, not the whole box.
Frequently Asked Questions
Do I need Docker to run WordPress on Proxmox VE?
No. This guide uses a plain LXC container with Apache, MariaDB, and PHP installed directly. Docker is a valid alternative, but for a single WordPress site it adds a layer of complexity you don't actually need.
How much RAM does a WordPress container really need?
512 MB is enough for a small personal blog with light traffic. 1–2 GB is more comfortable once you add caching plugins or start getting real visitors.
Can I run more than one WordPress site in the same container?
Yes, using Apache virtual hosts, but it's usually simpler and easier to troubleshoot if you give each site its own container.
Is it safe to expose an LXC-hosted WordPress site to the internet?
Keep the container unprivileged, keep everything patched, and put a reverse proxy with HTTPS in front of it instead of forwarding traffic directly. Don't skip the firewall on your router either.
MySQL or MariaDB — does it matter which one I use?
Not for WordPress. MariaDB is what Debian installs by default these days and it's fully compatible with WordPress's database layer, so there's no reason to seek out MySQL specifically.
Conclusion
You now have a self-hosted WordPress site running in an LXC container, built the same way a lot of real production sites still are — a plain Debian base, Apache, MariaDB, and PHP, with nothing hidden behind a one-click installer. That means when something eventually breaks, and something always eventually breaks, you'll actually know where to look.
From here, the natural next steps are a caching plugin to keep things snappy, a proper HTTPS setup if you're putting this online, and a snapshot schedule so an update gone wrong is a two-minute fix instead of a rebuild. Proxmox VE makes all three of those easy to add once the base container is running.