Self-hosted UniFi OS Server - single volume, no privileged mode, multi-arch, auto-updated
10K+
Self-hosted UniFi OS Server in Docker with a single volume, no privileged mode, and automatic updates.
Features:
privileged: true - uses explicit Linux capabilitieslinux/amd64 and linux/arm64latest - latest stable release5.1.15 - version-specific (UniFi OS Server version)⚠️ This image runs systemd as PID 1. It needs the
--cgroupns host, the--tmpfsmounts,cap_drop: ALL+ the explicitcap_addlist, and the/sys/fs/cgroupmount shown below. Drop any of these and systemd won't boot — the container stays up but no services start (see Troubleshooting). The host must use cgroup v2. Avoid deploying via GUIs (Portainer, etc.) — they frequently striptmpfs/cgroupns; use the compose file or the fulldocker runcommand below.
git clone https://github.com/toquanghieu/unifi-os-server-docker.git
cd unifi-os-server-docker
cp .env.example .env
# Edit .env and set UOS_SYSTEM_IP to your server's hostname or IP
docker compose up -d
docker run -d \
--name unifi-os-server \
--cgroupns host \
--cap-drop ALL \
--cap-add SYS_ADMIN --cap-add NET_ADMIN --cap-add NET_RAW \
--cap-add NET_BIND_SERVICE --cap-add DAC_OVERRIDE --cap-add DAC_READ_SEARCH \
--cap-add FOWNER --cap-add CHOWN --cap-add SETUID --cap-add SETGID \
--cap-add KILL --cap-add SYS_CHROOT --cap-add SYS_PTRACE \
--cap-add SYS_RESOURCE --cap-add AUDIT_WRITE --cap-add MKNOD \
--cap-add SETPCAP \
--tmpfs /run:exec --tmpfs /run/lock --tmpfs /tmp:exec \
--tmpfs /var/lib/journal --tmpfs /var/opt/unifi/tmp:size=64m \
-v /sys/fs/cgroup:/sys/fs/cgroup:rw \
-v unifi_data:/unifi \
-e UOS_SYSTEM_IP=your-server-ip \
-p 11443:443 -p 8080:8080 -p 3478:3478/udp -p 10003:10003/udp \
--restart unless-stopped \
hieutq/unifi-os-server:latest
Then open https://<your-server-ip>:11443 to complete setup.
To give the container its own IP on your LAN (devices reach it directly, no port mapping), attach it to a macvlan network instead of publishing ports:
# One-time: create the macvlan network (match your LAN subnet/gateway + host NIC)
docker network create -d macvlan \
--subnet 10.10.10.0/23 --gateway 10.10.10.1 \
-o parent=eth0 net1010
# Run on the macvlan — note: NO -p flags; the container owns its IP and all ports
docker run -d \
--name unifi-os-server \
--network net1010 --ip 10.10.10.56 \
--cgroupns host \
--cap-drop ALL \
--cap-add SYS_ADMIN --cap-add NET_ADMIN --cap-add NET_RAW \
--cap-add NET_BIND_SERVICE --cap-add DAC_OVERRIDE --cap-add DAC_READ_SEARCH \
--cap-add FOWNER --cap-add CHOWN --cap-add SETUID --cap-add SETGID \
--cap-add KILL --cap-add SYS_CHROOT --cap-add SYS_PTRACE \
--cap-add SYS_RESOURCE --cap-add AUDIT_WRITE --cap-add MKNOD \
--cap-add SETPCAP \
--tmpfs /run:exec --tmpfs /run/lock --tmpfs /tmp:exec \
--tmpfs /var/lib/journal --tmpfs /var/opt/unifi/tmp:size=64m \
-v /sys/fs/cgroup:/sys/fs/cgroup:rw \
-v unifi_data:/unifi \
-e UOS_SYSTEM_IP=10.10.10.56 \
--restart unless-stopped \
hieutq/unifi-os-server:latest
Then open https://10.10.10.56 (port 443 directly — no :11443).
Note: with macvlan the Docker host itself cannot reach the container's IP (kernel macvlan isolation) — access it from another machine on the LAN.
| Variable | Required | Description |
|---|---|---|
UOS_SYSTEM_IP | Yes | Hostname or IP for device adoption |
UOS_UUID | No | Fixed UUID (auto-generated if not set) |
| Protocol | Port | Default | Description |
|---|---|---|---|
| TCP | 11443 | Exposed | GUI/API (mapped from container 443) |
| TCP | 8080 | Exposed | Device communication |
| UDP | 3478 | Exposed | STUN |
| UDP | 10003 | Exposed | Device discovery |
| TCP | 8443 | Optional | Network app GUI |
| TCP | 8444 | Optional | Hotspot portal |
| UDP | 5514 | Optional | Syslog |
| TCP | 6789 | Optional | Mobile speed test |
| TCP | 5005 | Optional | RTP |
| TCP | 9543 | Optional | Identity Hub |
| TCP | 11084 | Optional | Site Supervisor |
| TCP | 5671 | Optional | AMQPS |
| TCP | 8880-8882 | Optional | Hotspot redirect |
All data is stored in a single Docker volume (unifi_data), organized as:
| Subdirectory | Contents |
|---|---|
data/ | Application data and UUID |
db/ | MongoDB database files |
config/ | UniFi configuration and system.properties |
logs/ | Service logs (nginx, mongodb, rabbitmq) |
srv/ | Service-specific data |
persistent/ | Package manager state |
rabbitmq-ssl/ | RabbitMQ SSL certificates |
To use a bind mount instead, replace unifi_data:/unifi with ./unifi:/unifi in the compose file and remove the volumes: section at the bottom.
ubnt/ubnt)set-inform http://<UOS_SYSTEM_IP>:8080/inform
This image does not use privileged: true. Instead:
cap_drop: ALL as baselineSYS_ADMIN, NET_ADMIN, NET_RAW, NET_BIND_SERVICE, DAC_OVERRIDE, DAC_READ_SEARCH, FOWNER, CHOWN, SETUID, SETGID, KILL, SYS_CHROOT, SYS_PTRACE, SYS_RESOURCE, AUDIT_WRITE, MKNOD, SETPCAPcgroup: host required for systemdThese capabilities are needed because UniFi OS Server runs 10+ internal services (MongoDB, PostgreSQL, RabbitMQ, Nginx, etc.) via systemd, each under different system users.
Container stays up but no services start — logs stop right after the
Setting UOS_SYSTEM_IP ... lines.
systemd (PID 1) failed to boot. Check inside the container:
docker exec <name> systemctl is-system-running
offline → a required flag is missing. Most often /run is not a tmpfs
(verify with docker exec <name> sh -c "mount | grep ' /run '" — it must say
tmpfs), or --cgroupns host is not set, or the /sys/fs/cgroup mount is
missing. Recreate the container with the full flag set from
Quick Start. A GUI deploy (Portainer, etc.) that silently
dropped tmpfs/cgroupns is the usual culprit.degraded is usually fine — dev-hugepages.mount,
sys-fs-fuse-connections.mount and systemd-logind always fail under Docker
and UOS works regardless. systemd-journald failing is not fine — see the
next entry.apt/dpkg always exit 2 inside the container, and UniFi updates never
install.
Symptom in /unifi/logs/apt/term.log:
Failed to create stream fd: Connection refused
dpkg: error: error executing hook '/usr/bin/systemd-cat -t ubnt-dpkg-status
/sbin/ubnt-dpkg-status-pre', exit code 256
UOS registers a dpkg pre-invoke hook that pipes through systemd-cat, so a
dead journal breaks every package operation — including the staged UniFi
Network upgrades uos-agent downloads to /unifi/data/uos/downloads. They fail
silently and retry forever.
Check the journal:
docker exec <name> systemctl status systemd-journald
status=218/CAPABILITIES means the container is missing CAP_SETPCAP.
journald (like logind and timedated) declares a CapabilityBoundingSet=,
and dropping bounding-set capabilities requires CAP_SETPCAP; without it the
unit dies before it ever starts. Add --cap-add SETPCAP (already in the
Quick Start flag set) and recreate the container — this is a
run-time flag, so rebuilding or re-pulling the image changes nothing.
unifi-core restart-loops every ~2s with no error message, and nothing ever
listens on 443.
The journal shows only this, about a second after each start:
unifi-core.service: Main process exited, code=dumped, status=6/ABRT
No JavaScript error, no V8 fatal report, no glibc message — the abort is completely silent. Check the task cap:
docker exec <name> systemctl show unifi-core -p TasksMax
If it is a low number (145 is common), that is the cause. unifi-core sets
UV_THREADPOOL_SIZE=256, so it needs ~269 tasks, and the Network app's JVM
needs ~162. When the cgroup refuses the 146th thread, libuv's init_threads()
calls a bare abort() — hence the silent SIGABRT, raised from
uv_fs_close → uv_once on the first async filesystem call.
TasksMax comes from systemd's DefaultTasksMax=15%, which is a share of the
host's kernel.pid_max/threads-max. A host that booted with little RAM —
a memory-ballooned Proxmox/VMware guest is the classic case, where
kernel.threads-max stays sized for the boot-time RAM — pushes it far below
what UOS needs. Images at/after this fix ship
/etc/systemd/system.conf.d/10-tasksmax.conf with DefaultTasksMax=infinity;
on an older image you can apply it live:
docker exec <name> sh -c 'mkdir -p /etc/systemd/system.conf.d && \
printf "[Manager]\nDefaultTasksMax=infinity\n" > /etc/systemd/system.conf.d/10-tasksmax.conf && \
systemctl daemon-reexec && systemctl restart unifi-core unifi'
"An unexpected error occurred during setup" in the web UI.
Pull :latest (or any tag at/after the fix) and recreate. Older images
reinstalled the standalone UniFi Network .deb over the firmware's integrated
build, which removed the ucore integration the setup wizard waits for:
docker pull hieutq/unifi-os-server:latest
# Install dependencies: binwalk, podman, docker
# Then extract and build:
./scripts/extract-image.sh uosserver-base:latest
docker build --build-arg BASE_IMAGE=uosserver-base:latest -t hieutq/unifi-os-server:local .
The extraction script automatically queries the Ubiquiti firmware API to find and download the latest installer for your architecture.
MIT
Content type
Image
Digest
sha256:20cffff82…
Size
741.5 MB
Last updated
23 days ago
docker pull hieutq/unifi-os-server