Security update: We discovered CVE-2026-54405 (CVSS 7.5). Ubiquiti has patched it—update UniFi Network to 10.4.57 or later.

Read the advisory

Running UniFi OS Server in Docker

Update (July 2026): we open-sourced this. Everything described in this post is now packaged as unihosted/unifi-os-server-docker — a prebuilt Docker image with a ready-to-run compose file, built by a fully transparent GitHub Actions pipeline. Skip to the Quick Start below to get UniFi OS Server running with a single docker compose up -d.

UniFi OS Server is Ubiquiti's network management platform that orchestrates UniFi Network, UniFi Identity, and other services. Ubiquiti only ships it as an OCI image embedded inside a proprietary installer designed for Podman — you can't simply pull it from a registry.

At Unihosted we were already running everything else in Docker, so we extracted that image and repackaged it to run cleanly under Docker. This post covers both the quick way (our open-source image) and the full technical journey of how we got there.

Quick Start

Save the compose below as docker-compose.yaml, set UOS_SYSTEM_IP to your public IP, and run docker compose up -d. The Web UI will be at https://localhost:11443 once the container is ready. Every port and environment variable is documented inline — the compose is the reference.

yaml
services:
  unifi-os-server:
    container_name: unifi-os-server
    image: ghcr.io/unihosted/unifi-os-server-docker:latest
    cgroup: host
    restart: unless-stopped
    cap_add:
      - NET_RAW
      - NET_ADMIN
    ports:
      - "8080:8080" # HTTP inform / redirect
      - "8443:8443" # UniFi Controller API
      - "8444:8444" # Secure Portal for Hotspot
      - "8880-8882:8880-8882" # Hotspot portal redirection (HTTP)
      - "10003:10003/udp" # UniFi discovery (Only needed on local network)
      - "11443:443" # UniFi Web UI (HTTPS)
      - "3478:3478/udp" # STUN used for device poking
      - "5514:5514/udp" # Syslog
      - "6789:6789" # Speed test

      # Network App bypass — skips UOS SSO, direct access to the controller
      # API on 127.0.0.1:8081.  Bind to localhost ONLY; never expose publicly.
      - "127.0.0.1:7443:7443"
      - "127.0.0.1:5432:5432" # PostgreSQL (localhost only)
    extra_hosts:
      - "host.docker.internal:host-gateway"
      - "host.containers.internal:host-gateway"
    volumes:
      - /sys/fs/cgroup:/sys/fs/cgroup:rw
      - ./docker/uos:/var/lib/uosserver
      - ./docker/uos:/var/lib/unifi
      - ./docker/data/:/data
    tmpfs:
      - /run:exec
      - /run/lock
      - /tmp:exec
      - /var/lib/journal
      - /var/opt/unifi/tmp:size=64m
      - /data/unifi-core/config/http
    networks:
      - unifi-os-server-network
    depends_on:
      - unifi-os-server-mongodb
    environment:
      TZ: Europe/Amsterdam
      UOS_SYSTEM_IP: "127.0.0.1"
      # SSO bypass for the Network App (port 7443).  Used by UniHosted for
      # debugging and direct API access.  NOT for production — the port is
      # bound to localhost above and must stay that way.
      EXPOSE_NETWORK_APP: "true"

      MONGO_INTERNAL: "false" # Defaults to false → uses the external MongoDB service below.
      # Set to "true" and remove the unifi-network-mongodb service + depends_on
      # to let the UniFi Network App run its own mongod (port 27117).

  unifi-os-server-mongodb:
    container_name: unifi-os-server-mongodb
    image: mongo:4.4
    networks:
      - unifi-os-server-network
    volumes:
      - ./docker/mongodb:/data/db

networks:
  unifi-os-server-network:

A few things worth knowing:

  • MongoDB runs as a separate service by default. The internal mongod is disabled by default; set MONGO_INTERNAL: "true" (and drop the MongoDB service) if you prefer the bundled one (port 27117).
  • No full privileged mode. The container runs with scoped capabilities (NET_RAW, NET_ADMIN) plus host cgroup access for systemd, instead of privileged: true.
  • The Network App bypass ports must stay bound to localhost. They skip UOS SSO and exist for debugging and direct API access.

The image, entrypoint, and the entire build pipeline live in the GitHub repository. If you'd rather use Ubiquiti's official Podman-based installer instead, see our UniFi OS Server download links post.

The rest of this post is the technical journey: how the image is extracted from Ubiquiti's installer and what it took to make it run under Docker.

The Challenge

UniFi OS Server comes packaged as a binary installer that embeds a container image. The installer is designed for Podman, not Docker, and the image format is OCI (Open Container Initiative) rather than Docker's native format. Additionally, UniFi OS Server has several unique requirements:

  • Systemd dependency: The platform relies on systemd for service management
  • Elevated network access: Requires extra capabilities for network configuration
  • Complex initialization: Multiple services need proper initialization and configuration
  • Persistent state: Extensive data directories that must survive container restarts

Step 1: Extracting the Base Image

The first challenge was extracting the container image from Ubiquiti's installer binary. The installer (e.g. unifi-os-server-download-506-x64) contains an embedded archive that we needed to extract.

Using Binwalk for Extraction

We used binwalk, a firmware analysis tool, to extract the embedded filesystem from the installer:

bash
binwalk --run-as=root -e unifi-os-server-download-506-x64

This extracts the contents to a directory (typically _unifi-os-server-download-506-x64.extracted/), revealing the embedded image.tar file containing the OCI-formatted container image.

Step 2: Converting OCI to Docker Format

The extracted image is in OCI format, which Docker can't directly load. We needed to convert it to Docker's format. While Docker and Podman share compatibility, the image format needed explicit conversion.

Podman as a Conversion Tool

Podman can handle both OCI and Docker formats, making it the perfect tool for conversion:

bash
# Load the OCI image into Podman
podman load -i image.tar

# Save it in Docker format
podman save --format docker-archive -o uosserver-0.0.54-docker.tar <image-name>

This creates a Docker-compatible image that can be loaded with docker load.

Step 3: Building the Docker Image

With the base image converted, we created a Dockerfile that extends it and adds our customizations:

dockerfile
FROM uosserver:0.0.54

ENV UOS_SERVER_VERSION="5.0.6"
ENV FIRMWARE_PLATFORM="linux-x64"

STOPSIGNAL SIGRTMIN+3

Key points:

  • STOPSIGNAL SIGRTMIN+3: This is systemd's shutdown signal, required for graceful service shutdown
  • Environment variables: Set the version and platform that UniFi OS Server expects

Step 4: From Manual Steps to an Automated Pipeline

Everything above started as manual work. Today it runs as a transparent GitHub Actions pipeline: the workflow downloads the official Ubiquiti installer, extracts the embedded OCI image, flattens its layers into a rootfs Docker can consume, and layers our own entrypoint on top. Nothing happens behind a curtain — you can inspect exactly what goes into the image at every step.

At runtime, the entrypoint configures MongoDB, wires up networking (a macvlan eth0 alias), exposes PostgreSQL, and hands off to systemd.

Key Configuration Points

Capabilities instead of privileged mode: Earlier iterations ran with privileged: true. The published image instead uses scoped capabilities (NET_RAW, NET_ADMIN) together with host cgroup access (cgroup: host and /sys/fs/cgroup mounted read-write) so systemd can function — a smaller attack surface than full privileged mode.

Volume Mounts: Persistence is required for /var/lib/uosserver and /var/lib/unifi (UniFi OS Server and Network configuration) and /data (application data for all services), plus a separate volume for MongoDB's data directory.

Port Mappings: UniFi OS Server exposes many ports — 443 for the web UI, 8080 for device inform, 8443 for the controller API, 3478/udp for STUN, plus hotspot portal, syslog, discovery, and speed-test ports. The Quick Start compose documents each one inline.

Architecture Insights

UniFi OS Server is a complex microservices architecture running multiple components:

  • UniFi Core: The core management service
  • UniFi Identity (UID): User identity and authentication
  • UniFi Link Platform (ULP): Service orchestration
  • UniFi Directory: Directory services
  • UCS Agent: Cloud services agent
  • MongoDB: Primary database
  • RabbitMQ: Message broker
  • PostgreSQL: Additional database backend
  • Nginx: Reverse proxy and web server

All these services are managed by systemd within the container, which is why the container needs host cgroup access and systemd-aware configuration.

Challenges and Solutions

Challenge 1: Systemd in Docker

Problem: Docker containers don't run systemd by default, but UniFi OS Server requires it.

Solution: Run /sbin/init as PID 1 with host cgroup access (cgroup: host, /sys/fs/cgroup mounted read-write) and tmpfs mounts for /run and /tmp, so systemd can manage its services without full privileged mode.

Challenge 2: Network Interface Expectations

Problem: UniFi OS Server expects eth0 to exist, but Docker creates differently named interfaces.

Solution: Create a macvlan interface alias to eth0 in the container startup configuration — this is why the container needs the NET_ADMIN capability.

Challenge 3: Persistent UUID

Problem: UniFi OS Server generates a UUID on first boot and expects it to persist.

Solution: Store the UUID in a persistent volume (/data/uos_uuid) and read it on subsequent starts.

Production Considerations

Security

The container needs elevated network capabilities and host cgroup access. Consider:

  • Keep the Network App bypass ports (7443, 5432) bound to localhost only
  • Network isolation and firewall rules in front of the exposed ports
  • Regular image updates
  • Monitoring and logging

Resource Requirements

UniFi OS Server is resource-intensive:

  • CPU: Multi-core recommended
  • Memory: At least 4GB, 8GB+ for production
  • Storage: Significant space needed for logs, databases, and firmware

Backup Strategy

Critical data to back up:

  • /var/lib/unifi and /var/lib/uosserver: UniFi configuration
  • MongoDB's data directory
  • /data: All service data

Updates

When Ubiquiti releases a new UniFi OS Server version, the build pipeline extracts and repackages it — updating means pulling the new image tag and recreating the container. As with any controller upgrade: back up first, and test in a non-production environment before rolling it out to infrastructure that manages real devices.

Conclusion

Running UniFi OS Server in Docker required solving several technical challenges: extracting the embedded image, converting formats, handling systemd, and managing complex initialization. The result is a containerized UniFi OS Server that can be deployed, scaled, and managed like any other Docker service — and it's now open source at unihosted/unifi-os-server-docker. Issues and pull requests are welcome; if it saves you a parallel Podman setup, a star helps others find it.

This approach is what enables us at Unihosted to provide UniFi OS Server as a managed service, with all the benefits of containerization: portability, version control, easy deployment, and infrastructure as code.

Don't want to run this yourself? Unihosted offers fully managed, hosted UniFi OS Server — no Docker, no binwalk, no systemd wrangling required. Get started with Unihosted and have your UniFi OS Server running in minutes.