If you've ever glanced at a Google Analytics dashboard and wondered exactly where all that visitor data actually goes, you're not alone. A lot of homelab owners and small business admins eventually want the same traffic graphs and visitor counts, minus the part where a third party owns the data. That's the whole pitch behind Matomo: an open-source analytics platform you run yourself, on your own server, under your own roof.
This guide walks you through installing Matomo inside an LXC container on Proxmox VE, using Docker Compose to run Matomo and its MariaDB database side by side. You don't need any prior Docker experience, and you don't need to be a Linux wizard. You do need about 30 to 45 minutes and a Proxmox VE host with a little spare RAM.
What You Will Learn
By the end of this tutorial, you'll have a working Matomo instance running in its own isolated LXC container, reachable from your browser, and ready to start counting real visitors. Specifically, you'll learn how to:
- Create a Debian 13 LXC container sized correctly for Matomo
- Enable the nesting feature so Docker can run cleanly inside an unprivileged container
- Install Docker and Docker Compose inside the container
- Write a docker-compose.yml file that pairs Matomo with a MariaDB database
- Finish the Matomo setup wizard and create your first tracked website
- Set up automatic report archiving with a cron job, which most beginners skip and later regret
What Is This Feature?
Matomo (formerly known as Piwik) is a web analytics application. It tracks visits, page views, referral sources, devices, and rough visitor locations on any website you add a tracking snippet to — the same kind of information Google Analytics collects, except the data lives in a database you control.
To actually run Matomo, you need somewhere to host it. That's where Proxmox VE and LXC come in. An LXC container is a lightweight virtual environment that shares the host's Linux kernel instead of emulating an entire virtual machine. It starts in a couple of seconds, uses less RAM than a comparable VM, and is a good fit for a single web application like Matomo that doesn't need its own kernel or full hardware emulation.
Inside that container, we're going to use Docker. Docker packages an application and everything it needs — PHP, its libraries, configuration — into a self-contained image that runs the same way every time. Instead of manually installing PHP, Apache, and all of Matomo's dependencies by hand and hoping the versions play nicely together, Docker Compose lets you describe two services (Matomo and its database) in one text file and bring them both up with a single command.
Why Would You Use It?
Google Analytics is free, well documented, and works out of the box, so it's worth asking why anyone would bother self-hosting an alternative. A few reasons come up again and again in the Proxmox and homelab community:
- Data ownership. Every row of visitor data stays on your storage, not on someone else's servers.
- No sampling, no surprise limits. Matomo doesn't sample your data once you cross a traffic threshold the way some hosted tools do.
- Privacy compliance. Because you control the data and retention, Matomo is a common pick for sites that need to avoid sending visitor data to a US-based third party under GDPR.
- It's genuinely useful learning. Running a real multi-container app (web server plus database) on Proxmox VE is a solid, low-risk way to get comfortable with Docker Compose before you use it for something more important.
None of that means Matomo is strictly better than Google Analytics for every use case. If you run a dozen client sites and don't want to babysit updates and backups, a hosted tool is genuinely less work. But if you already run a Proxmox VE box at home, adding one more small container is cheap, and you get to keep the data.
Prerequisites
Before you start, make sure you have the following in place:
- A working Proxmox VE 8.x or 9.x host with a bit of free storage (the container itself needs about 8 GB) and 2 GB of RAM you're not already using
- A Debian 13 (or Debian 12) LXC template downloaded — you can grab this from Datacenter → your node → local (Storage) → CT Templates → Templates if it isn't already there
- Basic comfort using the Proxmox VE web interface and a terminal inside a container shell
- A website you actually want to track, even if it's just a test page for now — you'll need somewhere to paste the tracking code at the end
You don't need a domain name or a TLS certificate to follow along. We'll access Matomo over plain HTTP on your local network first; putting it behind a reverse proxy with HTTPS is a good next step once things are working, but it's outside the scope of this guide.
Step-by-Step Tutorial
Step 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: something like
matomo - Unprivileged container: leave this checked. There's no good reason to run Matomo as a privileged container.
- Template: Debian 13 (Trixie)
- Disk size: 8 GB is enough to start; you can grow it later if your analytics database gets large
- CPU cores: 2
- Memory: 2048 MB, plus a small swap allocation (512 MB is plenty)
- Network: attach it to your usual bridge (
vmbr0in most setups) with DHCP or a static IP, whichever you normally use
Don't start the container yet. Before you do, there's one setting that trips up almost everyone trying Docker inside LXC for the first time.
Step 2: Enable Nesting
Docker needs to create its own network namespaces and cgroups, which an LXC container doesn't allow by default for security reasons. Select your new container, go to Options → Features, click Edit, and check the box labeled Nesting. Click OK.
Skip this step and Docker will either refuse to start or fail partway through pulling images with cryptic permission errors. It's a two-second checkbox that saves you twenty minutes of confused troubleshooting.
Now start the container and open its console (or SSH in once networking is up).
Step 3: Update the Container and Install Docker
First, update the package list and installed packages:
apt update && apt upgrade -y
Install the packages Docker's installer needs to fetch and verify its own repository:
apt install -y ca-certificates curl gnupg
Add Docker's official GPG key and repository:
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/null
Now install Docker Engine along with the Compose plugin:
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Confirm it's running:
docker --version
docker compose version
You should see version numbers printed for both. If you get a "Cannot connect to the Docker daemon" error here, jump ahead to the troubleshooting section — nine times out of ten it means nesting wasn't actually enabled before the container started.
Step 4: Write the Docker Compose File
Create a directory to keep everything organized, and move into it:
mkdir -p /opt/matomo && cd /opt/matomo
Create a file named docker-compose.yml with the following content. You can use nano docker-compose.yml if you're not comfortable with vim.
services:
db:
image: mariadb:11.4
restart: unless-stopped
volumes:
- db_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: change_this_root_password
MYSQL_DATABASE: matomo
MYSQL_USER: matomo
MYSQL_PASSWORD: change_this_password
matomo:
image: matomo:latest
restart: unless-stopped
ports:
- "8080:80"
volumes:
- matomo_data:/var/www/html
environment:
MATOMO_DATABASE_HOST: db
MATOMO_DATABASE_ADAPTER: mysql
MATOMO_DATABASE_TABLES_PREFIX: matomo_
MATOMO_DATABASE_USERNAME: matomo
MATOMO_DATABASE_PASSWORD: change_this_password
MATOMO_DATABASE_DBNAME: matomo
depends_on:
- db
volumes:
db_data:
matomo_data:
Before saving, change change_this_root_password and change_this_password to actual passwords. The two MYSQL_PASSWORD / MATOMO_DATABASE_PASSWORD values need to match each other exactly — that's how Matomo authenticates against the database.
Step 5: Start the Stack
From inside /opt/matomo, run:
docker compose up -d
Docker will pull the Matomo and MariaDB images, which takes a few minutes on the first run depending on your internet connection — the Matomo image alone is a few hundred megabytes. Once it finishes, check that both containers are actually running:
docker compose ps
You should see two services listed with a status of running or healthy.
Step 6: Run the Matomo Setup Wizard
Find your container's IP address (either from the Proxmox VE summary tab or by running ip a inside the container), then open http://<container-ip>:8080 in your browser. Matomo's setup wizard will walk you through:
- A welcome screen and language selection
- A system check — it should pass automatically since the Docker image already includes everything Matomo needs
- Database setup, where you enter the same database name, username, and password you put in your compose file
- Table creation, which happens automatically once you click through
- Creating your Matomo super user account (this is your admin login, separate from anything on the Proxmox host)
- Setting up your first website, where you give it a name and paste in its real URL
- A tracking code screen, showing the JavaScript snippet to paste before the closing
</head>tag on the site you want to track
Once you save that tracking code into your website and load a page, refresh the Matomo dashboard and you should see your first visit appear, usually within a few seconds.
Commands Explained
A quick rundown of what each command you just ran actually does:
| Command | What it does |
|---|---|
apt update && apt upgrade -y | Refreshes the list of available packages and installs any pending updates so you're not building on an outdated base image. |
curl -fsSL ... -o /etc/apt/keyrings/docker.asc | Downloads Docker's signing key so apt can verify packages actually came from Docker, not a tampered mirror. |
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin | Installs the Docker engine itself, its command-line client, the container runtime it depends on, and the Compose plugin used for multi-container apps like this one. |
docker compose up -d | Reads docker-compose.yml, creates both containers and their shared network, and starts them in the background (that's what -d, or "detached," means). |
docker compose ps | Lists the containers defined in the current compose file along with their status, so you can confirm they're actually up before troubleshooting. |
docker compose logs matomo | Shows recent log output from the Matomo container — the first place to look when something isn't loading. |
Common Errors
A handful of problems account for almost every stuck Matomo-on-LXC install:
"Cannot connect to the Docker daemon at unix:///var/run/docker.sock" — Docker's service isn't running, usually because nesting wasn't enabled before the container was created. Enabling nesting on an already-running container doesn't help; you have to stop the container, enable it under Features, then start the container again.
Matomo's system check page shows a red X next to database connectivity — almost always a typo between the password in docker-compose.yml and what you typed into the wizard, or the db container hasn't finished starting yet. Give it another 15-20 seconds and refresh; MariaDB's first boot takes a moment to initialize its data directory.
Port 8080 refuses to connect from your browser — check that the container's firewall (if you enabled the Proxmox VE firewall on this container) isn't blocking inbound traffic on 8080, and confirm you're using the container's IP, not the Proxmox host's IP.
"exec /usr/local/bin/docker-entrypoint.sh: no such file or directory" — this one shows up occasionally on ARM-based Proxmox hosts (like a Raspberry Pi) when Docker pulls the wrong image architecture. Add platform: linux/amd64 or platform: linux/arm64 under the relevant service in your compose file, matching your actual host CPU.
Troubleshooting
If containers start but Matomo's web page never loads, check the logs first instead of guessing:
docker compose logs -f matomo
The -f flag "follows" the log, streaming new lines as they appear, which is useful while you reload the page in your browser and watch what happens in real time. Press Ctrl+C to stop following.
If the database container keeps restarting in a loop, check its logs the same way with docker compose logs db. A common cause is reusing an old db_data volume from a previous attempt where the root password doesn't match what's currently in your compose file — MariaDB only sets the root password on its very first startup, so changing it later in the YAML file does nothing to an existing volume. If you need to start clean, run docker compose down -v to remove the containers and their volumes together, then docker compose up -d again.
If you can reach Matomo but pages load painfully slowly, check how much RAM the container actually has free with free -h run inside the LXC shell. Matomo's PHP processes and MariaDB both want headroom; if you're pinned at 2 GB and swapping heavily, bump the container's memory up in Proxmox VE → your container → Resources → Memory.
Best Practices
A few things worth doing once the basic install is working:
- Set up cron-based archiving. By default, Matomo calculates reports on the fly when you view them, which gets slow as your traffic grows. Add a cron job that runs
docker compose exec matomo /usr/local/bin/php /var/www/html/console core:archiveevery hour, and turn off browser-triggered archiving in Matomo's admin settings under Administration → System → General Settings. - Back up both volumes, not just the database. The
matomo_datavolume holds your configuration and plugins; thedb_datavolume holds your actual visitor data. A Proxmox VE backup of the whole container covers both automatically, which is one more reason to run this in its own dedicated LXC container rather than bundling it with other apps. - Put it behind a reverse proxy with HTTPS before you send real production traffic to it, especially if you're tracking a site with logged-in users. Sending analytics data over plain HTTP on the open internet isn't something you want long-term.
- Pin your image versions once you're happy with a working setup. Using
matomo:latestis fine for this first install, but once things are stable, switching to a specific tag likematomo:5.12means an unrelated image update won't change your setup out from under you. - Don't run this as a privileged container. Nesting works fine on an unprivileged container for this use case, and there's no upside to the extra risk of privileged mode here.
Frequently Asked Questions
Do I need a VM instead of an LXC container for this?
No. Matomo and MariaDB are ordinary Linux processes with no special kernel or hardware requirements, which is exactly the kind of workload LXC containers handle well. A VM would work too, but it's more overhead for no real benefit here.
Can I run Matomo without Docker, directly on the container?
Yes, Matomo can be installed on bare Debian with Apache or Nginx, PHP, and MariaDB installed the traditional way. Docker Compose just packages all three pieces with matched, tested versions so you're not chasing PHP version mismatches yourself.
How much traffic can this handle on a 2 GB container?
Comfortably tens of thousands of visits a month for most small to mid-sized sites. If you're tracking something with real scale, budget more RAM and keep an eye on the cron archiving job — that's usually what falls behind first, not the tracking itself.
Is Matomo actually free?
The self-hosted version you just installed is free and open source under the GPL. Matomo also sells a hosted cloud version and paid plugins, but nothing in this tutorial requires either.
What happens if I lose the container?
You lose your analytics history unless you've backed it up. Use Proxmox VE's built-in backup jobs (vzdump) against this container the same way you would for any other, and store at least one copy somewhere other than the same physical disk.
Conclusion
You now have a working, self-hosted analytics platform running in a lightweight LXC container, completely separate from anything Google sees. The setup here is intentionally minimal — one container, two services, no reverse proxy yet — which makes it a good foundation to build on rather than a finished production deployment.
From here, the next reasonable steps are putting Matomo behind an HTTPS reverse proxy, wiring up that cron archiving job so reports stay fast as data grows, and adding this container to your regular Proxmox VE backup schedule. None of that is required to start tracking visits today, but all of it matters once you're relying on the data.