unifi-os-server and the awkward requirement to host a systemd appliance in a container
Run UniFi OS Server in Docker or Kubernetes
At a glance
- What is it?
- unifi-os-server runs the proprietary UniFi OS Server inside Docker, Kubernetes or a Home Assistant add-on, and the engineering problem it solves is structural: UniFi OS Server is a systemd based appliance, so the container needs the host cgroup hierarchy, a set of capabilities and a long list of ingress ports. It fits a self hoster with a UniFi controller to place, and it is not a supported Ubiquiti deployment.
- Who is it for?
- unifi-os-server fits a self hoster who needs a UniFi Network controller on infrastructure they control, who is comfortable with a container that has access to the host cgroup hierarchy and extra capabilities, and who reads the compatibility notes before upgrading Ubiquiti software.
- Can I use it commercially?
- Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
UniFi OS Server replaced the Network Server, and that changed the job
The project exists because of a change on Ubiquiti's side, and understanding that change explains everything about the container configuration.
The readme quotes the vendor's own framing: the UniFi OS Server is the new standard for self-hosting UniFi, replacing the legacy UniFi Network Server. While the Network Server provided basic hosting functionality, it lacked support for key UniFi OS features like Organizations, IdP Integration, or Site Magic SD-WAN. With a fully unified operating system, UniFi OS Server now delivers the same management experience as UniFi native, including CloudKeys, Cloud Gateways and Official UniFi Hosting, and is fully compatible with Site Manager for centralized, multi-site control.
So the artifact being containerised is not a Java application with a REST API. It is a full appliance operating system, and that is the source of every awkward requirement in the repository.
Two ecosystems are involved and the readme is clear about both. The `uosserver` image is provided by UniFi, extracted from the installation binary. The `unifi-os-server` image, the one this project builds, provides better compatibility for Docker and Kubernetes with directory fixes and configuration through environment variables. The Dockerfile confirms the base: it is `FROM ghcr.io/lemker/uosserver:c9603dec9010-multiarch`, a multi-architecture image tagged with a commit hash.
The legal position is stated plainly as well. The project is not affiliated with, endorsed by or supported by Ubiquiti Inc. UniFi, the UniFi logo and UniFi OS are trademarks of Ubiquiti Inc. used only to identify the software this project runs. The project is licensed under the GNU Affero General Public License v3.0, and UniFi OS Server itself is proprietary software owned by Ubiquiti Inc. and subject to its own licence terms.
That combination, AGPL tooling wrapping a proprietary appliance, is the shape of the whole project.
The compose file is a systemd appliance in a container, and it shows
The compose file in the repository is the clearest description of what the container needs, and it is unusual enough to be worth reading closely.
services:
unifi-os-server:
image: ghcr.io/lemker/unifi-os-server:latest
container_name: unifi-os-server
cgroup: host
cap_add:
- NET_RAW
- NET_ADMIN
extra_hosts:
host.docker.internal: host-gateway
tmpfs:
- /run:exec
- /run/lock
- /tmp:exec
- /var/lib/journal
- /var/opt/unifi/tmp:size=64mThree of those settings exist because of systemd, and the readme explains the requirement in the FAQ: the underlying structure of UniFi OS Server runs every component as systemd services, which requires access to the host `cgroup`.
That is why `cgroup: host` is set. In a normal container, the cgroup namespace is namespaced and systemd inside the container cannot manage the units it needs. The host cgroup hierarchy is then bind mounted read-write at `/sys/fs/cgroup`.
The `tmpfs` list is the second systemd consequence. A service manager needs writable runtime directories, and `/run`, `/run/lock`, `/tmp` and `/var/lib/journal` are mounted as tmpfs, with the last one given a size limit of 64m. Those are the directories systemd writes to at runtime, and mounting them as tmpfs keeps that state out of the image layers and out of the writable volumes.
The capabilities are the third. `NET_RAW` and `NET_ADMIN` are needed because the appliance creates its own network interfaces and raw sockets, and the extra host entry mapping `host.docker.internal` to the host gateway lets the container reach the host by name.
The `STOPSIGNAL SIGRTMIN+3` in the Dockerfile is the fourth systemd artefact, a stop signal that systemd inside the container understands as a clean shutdown, and the `systemctl mask` line in the Dockerfile hides the getty targets so the container does not try to start a login console.
The volume list is long for the same reason. Persistent state is split across `/persistent`, `/var/log`, `/data`, `/srv`, `/var/lib/unifi`, `/var/lib/mongodb` and `/etc/rabbitmq/ssl`, because UniFi OS Server is a bundle of several services, each with its own state directory, and the container has to persist all of them separately.
The port list is a map of the appliance, not of your application
The ports table in the readme is the clearest way to see what UniFi OS Server actually is, because each port corresponds to a subsystem rather than to an application endpoint.
The primary one is TCP 11443 on ingress for the UniFi OS Server GUI and API. Then TCP 5005 for the RTP real-time transport control protocol, TCP 9543 for the UniFi Identity Hub, TCP 6789 for the UniFi mobile speed test, TCP 8080 for device and application communication, and TCP 8444 for the Secure Portal for Hotspot.
The UDP entries are the discovery and remote management ones. UDP 3478 is STUN for device adoption and communication, and the note says it is also required for Remote Management. UDP 5514 is remote syslog capture, and UDP 10003 is device discovery during adoption.
Then come the Hotspot and support ports: TCP 28082 for device support files download, TCP 5671 for AQMPS, and TCP 8880, 8881 and 8882 for Hotspot portal redirection.
What this list tells you is that the container is a network participant, not a web application behind a reverse proxy. Device adoption means the controller has to be reachable on the discovery ports and the adopt flow requires the device to point at the server, which is what `UOS_SYSTEM_IP` is for. To adopt a device you SSH into it with the username and password `ubnt`/`ubnt`, and set the inform address with `set-inform http://$UOS_SYSTEM_IP:8080/inform`.
So a deployment is not finished when the GUI loads. Devices have to be told where the controller is, and the ports for that conversation have to be open on the network path between the devices and the server. That is the operational difference between this project and running a containerised Java application.
Kubernetes needs the ingress controller changed, which is the part teams miss
The Kubernetes section points at a `kubernetes` directory in the repository and says the deployment example uses ingress-nginx for the ingress and longhorn for storage. Then it states the requirement that trips people up: your ingress controller must be modified to accept extra ports, and it gives the Helm values.
tcp:
5005: "unifi/unifi-os-server-rtp-svc:5005" # Optional
9543: "unifi/unifi-os-server-id-hub-svc:9543" # Optional
8080: "unifi/unifi-os-server-communication-svc:8080"
udp:
3478: "unifi/unifi-os-server-stun-svc:3478"
10003: "unifi/unifi-os-server-discovery-svc:10003"An ingress controller by default terminates HTTP and forwards it. A device adoption conversation over raw UDP, or the device-to-controller communication on 8080, is not HTTP traffic, and the controller will not forward it unless it is explicitly told to.
That is why the values block maps a port to a service name rather than to a host, and why the services are named after the component they belong to. The `unifi-os-server-rtp-svc`, `unifi-os-server-id-hub-svc`, `unifi-os-server-communication-svc`, `unifi-os-server-stun-svc` and `unifi-os-server-discovery-svc` names are per-subsystem, which is a hint about how the deployment is structured internally.
The optional lines are also informative. Most of the port mappings are marked optional, and the ones that are not, 8080 for device and application communication and the UDP discovery and STUN ports, are the ones a working adoption needs. The others exist for features such as the hotspot portal, the identity hub and the mobile speed test, which a deployment that does not use those features can leave closed.
So the Kubernetes path is a real modification to your cluster's networking, not a manifest you apply. That is the single most useful thing to know before you start.
The Home Assistant add-on is the least demanding way to run it
There is a third deployment path, and it is the one with the fewest sharp edges because the orchestrator does most of the work for you.
The Home Assistant route is an add-on. In Home Assistant you go to Settings, then Apps, then App store, click the menu, then Repositories, fill in `https://github.com/lemker/unifi-os-server`, and click Add and Close. Then you click the UniFi OS Server app, click Install and wait until it is installed.
Two optional settings follow. You can set `UOS_SYSTEM_IP` on the Configuration tab, and if it is left empty the app uses the Home Assistant IP address. Then on the Info tab you click Start, wait a few minutes and click Open Web UI. The readme points at `ha-app/DOCS.md` for storage, ports and security notes.
The reason this path is easier is that Home Assistant add-ons run under a supervisor that already handles the awkward parts. The cgroup access, the tmpfs mounts and the capabilities are declared by the add-on configuration rather than by you, so the class of problem that dominates the Docker and Kubernetes paths, a systemd appliance that needs the host cgroup, is handled by the platform.
That said, the constraint does not disappear, it moves. The add-on is still running the same appliance with the same port requirements, and the readme's own pointer to a DOCS.md for ports and security notes suggests the same caveats apply. It also binds you to Home Assistant as the host, which is a fine choice for a home lab and a strange one for a server room.
The `UOS_SYSTEM_IP` variable is worth a note regardless of the deployment path, because the readme says it is the hostname, recommended, or IP address for inform, and the same variable appears in the compose file as `UOS_SYSTEM_IP=unifi.example.com`.
What breaks when Ubiquiti ships a new version
The project pins a specific application version in its Dockerfile, and that is the thing to watch.
The Dockerfile sets `ENV APP_VERSION="5.1.42"` and `ENV APP_MODEL="UOSSERVER"` and `ENV PRODUCT_NAME="UniFi OS Server"`, and the base image is tagged with a commit hash rather than a version number, `ghcr.io/lemker/uosserver:c9603dec9010-multiarch`. The readme says the `uosserver` image is extracted from the installation binary, which means the upstream artefact is a downloadable installer that has to be repackaged.
That is where the fragility lives. A container built from an extracted installer of a specific version does not automatically gain the fixes in a newer one. If Ubiquiti releases a new UniFi OS Server, this project has to extract the new binary, rebuild the base image, and publish a new release, and until that happens the version you are running is frozen at whatever it was.
The release history shows a steady cadence: v1.6.0 on 2026-09-02, v1.7.0 on 2026-09-10, and v1.8.0 on 2026-10-13, with the last push to the branch on 2026-10-20. So the project is being maintained and is tracking releases, which is the reassuring half of the picture.
The other half is the environment variables. There are only two, `UOS_SYSTEM_IP` for the hostname or IP for inform, and `HARDWARE_PLATFORM` to override the detected hardware platform, with the only accepted value being `synology`. The existence of that override is a hint about the constraints of the container environment, since some platforms report hardware in a way the detection does not recognise.
And the readme is explicit that the container needs specific cgroup and tmpfs settings, and that the reason is the systemd service structure. That is a permanent characteristic of running this appliance this way, not a version specific problem.
Editorial conclusion
unifi-os-server fits a self hoster who needs a UniFi Network controller on infrastructure they control, who is comfortable with a container that has access to the host cgroup hierarchy and extra capabilities, and who reads the compatibility notes before upgrading Ubiquiti software. It is the wrong choice if you want a supported Ubiquiti deployment, since the project states it is not affiliated with, endorsed by or supported by Ubiquiti, and it is the wrong choice if your orchestrator cannot give a container cgroup and capability access, since the systemd services inside will not start without them. Before you deploy, verify three things: that your UniFi OS Server version is compatible with the container image, since the project pins an app version of 5.1.42 in its Dockerfile, that your ingress controller accepts the extra TCP and UDP ports, and that the cgroup and tmpfs settings in the compose file are reproduced in your environment. The code is AGPL-3.0 and the UniFi OS Server software it runs remains proprietary.
Frequently asked questions
What is the unifi-os-server project?
It is a way to run the proprietary UniFi OS Server directly in Docker or Kubernetes, and it is also published as a Home Assistant add-on. The project provides its own `unifi-os-server` image with directory fixes and configuration through environment variables, layered on the `uosserver` image that UniFi provides.
Why does the unifi-os-server container need special cgroup and tmpfs settings?
Because the underlying structure of UniFi OS Server runs every component as systemd services, which requires access to the host `cgroup`. The compose file therefore sets `cgroup: host`, bind mounts `/sys/fs/cgroup` read-write, and mounts `/run`, `/run/lock`, `/tmp` and `/var/lib/journal` as tmpfs.
Which ports does the unifi-os-server project expose?
TCP 11443 for the GUI and API, TCP 8080 for device and application communication, TCP 5005 for RTP control, TCP 9543 for the Identity Hub, TCP 6789 for the mobile speed test, TCP 8444 for the Secure Portal for Hotspot, TCP 28082 for support files, TCP 5671 for AQMPS, TCP 8880 to 8882 for Hotspot redirection, and UDP 3478 for STUN, UDP 5514 for syslog and UDP 10003 for device discovery.
How do I adopt a device with unifi-os-server?
Set `UOS_SYSTEM_IP` to the hostname, which the documentation recommends, or to the IP address. Then SSH into the device with the username and password `ubnt`/`ubnt` and set the inform address with `set-inform http://$UOS_SYSTEM_IP:8080/inform`.
Is unifi-os-server supported by Ubiquiti?
No. The readme states the project is not affiliated with, endorsed by or supported by Ubiquiti Inc. UniFi, the UniFi logo and UniFi OS are trademarks of Ubiquiti Inc. used only to identify the software the project runs, and the project itself is licensed under the AGPL v3.0 while UniFi OS Server remains proprietary under its own licence terms.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/lemker-unifi-os-server)Was this useful?