Running Unifi Network Application in an Incus container
Setting up the Unifi Network Application and MongoDB using Quadlets in an Alma Linux Incus container.

In my previous post I set up an Incus node.

Now i want to start running system containers in Incus. The first container i'm going to run is for the Unifi Network Application and the associated Mongo database.
The container will have its own IP on the local network with the Unifi and MongoDB services run via Podman.
I'm using an Alma Linux 9 container as the point releases ship with a modern version of Podman.
The Incus container
First log into the Incus node and create the container.
incus launch images:almalinux/9 unifi --profile bridged
Now let's get the MAC address of the Unifi container
incus info unifi
Find the MAC address and then reserve an IP address for that MAC on your router.
Configuring the Unifi container
Get a shell on the container to begin configuration
incus shell unifi
Install an editor and Podman
dnf install nano podman -y
The Unifi Network Application expects a pre-existing Mongo database so let's set that up now.
MongoDB
Create the MongoDB initialisation script
nano -w init-mongo.sh
and copy/paste the following:
#!/bin/bash
if which mongosh > /dev/null 2>&1; then
mongo_init_bin='mongosh'
else
mongo_init_bin='mongo'
fi
"${mongo_init_bin}" <<EOF
use ${MONGO_AUTHSOURCE}
db.auth("${MONGO_INITDB_ROOT_USERNAME}", "${MONGO_INITDB_ROOT_PASSWORD}")
db.createUser({
user: "${MONGO_USER}",
pwd: "${MONGO_PASS}",
roles: [
{ db: "${MONGO_DBNAME}", role: "dbOwner" },
{ db: "${MONGO_DBNAME}_stat", role: "dbOwner" }
]
})
EOF
This script comes from the LinuxServer page for the Unifi Network Application Docker image.
Now create a volume for the DB
podman volume create unifi-db
and launch MongoDB for initialisation
podman run --rm \
-e MONGO_INITDB_ROOT_USERNAME=root \
-e MONGO_INITDB_ROOT_PASSWORD=itsasecret \
-e MONGO_USER=unifi \
-e MONGO_PASS=itsasecret \
-e MONGO_DBNAME=unifi \
-e MONGO_AUTHSOURCE=admin \
-v unifi-db:/data/db:Z \
-v /root/init-mongo.sh:/docker-entrypoint-initdb.d/init-mongo.sh:ro \
docker.io/mongo:7.0
Change the passwords as you see fit, but remember them because you'll need them later.
Wait a minute while the DB is initialised then do a ctrl-c
to shut things down and rm init-mongo.sh
to remove the initialisation script.
Quadlet files
Now that the database is ready it's time to set up the Unifi and Unifi-DB OCI containers. We're going to use Quadlet files for this.
First change directories
cd /etc/containers/systemd
We have several files to create. Create the files as named then copy/paste the contents:
unifi-app.volume
[Volume]
VolumeName=unifi-app
unifi-db.volume
[Volume]
VolumeName=unifi-db
unifi.pod
[Pod]
PodName=unifi
PublishPort=8443:8443
PublishPort=8080:8080
PublishPort=3478:3478/udp
PublishPort=10001:10001/udp
PublishPort=1900:1900/udp
PublishPort=8843:8843
PublishPort=8880:8880
PublishPort=6789:6789
PublishPort=5514:5514/udp
[Install]
WantedBy=multi-user.target default.target
unifi-app.container
[Container]
ContainerName=unifi-app
Image=lscr.io/linuxserver/unifi-network-application:latest
AutoUpdate=registry
Environment=MONGO_USER=unifi
Environment=MONGO_PASS=itsasecret
Environment=MONGO_DBNAME=unifi
Environment=MONGO_AUTHSOURCE=admin
Environment=MONGO_HOST=unifi-db
Environment=MONGO_PORT=27017
Environment=MEM_LIMIT=1024
Environment=MEM_STARTUP=1024
Environment=TZ=Europe/London
Volume=unifi-app.volume:/config:Z
Pod=unifi.pod
[Unit]
After=unifi-db.service
unifi-db.container
[Container]
ContainerName=unifi-db
Image=docker.io/mongo:7.0
AutoUpdate=registry
Environment=MONGO_INITDB_ROOT_USERNAME=root
Environment=MONGO_INITDB_ROOT_PASSWORD=itsasecret
Environment=TZ=Europe/London
Volume=unifi-db.volume:/data/db:Z
Pod=unifi.pod
Finally set up automatic updates for your containers
systemctl enable podman-auto-update.{service,timer}
Now let's exit the container shell
exit
and restart it so that it can get its new IP from the router and start the Unifi services
incus restart unifi
Go to https://CONTAINER-IP-ADDRESS:8443 to setup Unifi
Extras (Tailscale, TLS, System Updates)
The above gets you a functional Unifi Network Application setup but i want a bit more. I want to access my Unifi controller from anywhere, i don't want TLS certificate warnings and i want the system container to stay up to date. Here's how to do that.
Get a shell on your Unifi container
incus shell unifi
We're going to use Caddy as a reverse proxy for the Unifi web server. Caddy has built-in support for getting LetsEncrypt certificates, including using Tailscale integration.
Add the Caddy and Tailscale repos then install them along with automatic updates
dnf install 'dnf-command(copr)' -y
dnf copr enable @caddy/caddy -y
dnf config-manager --add-repo https://pkgs.tailscale.com/stable/centos/9/tailscale.repo
dnf install tailscale caddy dnf-automatic -y
Edit /etc/default/tailscaled
and add TS_PERMIT_CERT_UID=caddy
to the bottom.
Enable and authenticate Tailscale
systemctl enable --now tailscaled
tailscale up
Create/edit /etc/caddy/Caddyfile
replacing TAILNET-NAME to be your actual tailnet name:
unifi.TAILNET-NAME.ts.net {
file_server
reverse_proxy https://localhost:8443 {
transport http {
tls
tls_insecure_skip_verify
}
}
}
Enable Caddy
systemctl enable --now caddy
After a minute or so Caddy should get a certificate and you can now access your Unifi controller at https://unifi.TAILNET-NAME.ts.net. If your router supports it you can set up a DNS override for unifi.TAILNET-NAME.ts.net
so you can still access the controller at this address in the event your Internet or Tailscale is down.
For keeping the system container updated edit /etc/dnf/automatic.conf
and change apply_updates
to yes
.
Then enable the automatic updates
systemctl enable --now dnf-automatic.timer
Container snapshot
Now that the Unifi container is running as we want let's create snapshot of the known good state so we can roll back if future updates break things.
On the Incus node
incus snapshot create unifi initial