↓ Skip to main content
  1. Posts/
  2. Docker UI/

Arcane Docker Manager - Setup and Portainer Alternative

Stilicho2011
Author
Stilicho2011
Writing about homelab, self-hosting, automation and open-source solutions
Table of Contents
Docker UI Panels - This article is part of a series.
Part : This Article

What is Arcane
#

Arcane is an open-source (BSD-3-Clause) web UI for Docker, pitched as a modern Portainer alternative. It’s still a fairly young project, though it’s not brand new either, and it’s growing fast - the GitHub repo has already cleared six thousand stars, and the feature list is genuinely impressive: containers, images, volumes, networks, Docker Swarm support, one-click templates, remote environments via agents, image vulnerability scanning, RBAC, and OIDC single sign-on.

Arcane sits in the gap between Dockge, which is basically married to docker-compose and can’t really manage images, networks, or volumes properly (and, let’s be honest, the project’s more dead than alive at this point), and Portainer, which plenty of people find bloated for a home lab.

So Arcane is going for a middle ground: a clean, modern UI that still gives you full control over everything Docker can do.

If you’ve used Komodo or Portainer before, the philosophical difference with Arcane is that it’s first and foremost a UI sitting on top of the Docker Engine on a given host (plus agents for remote hosts), not a full CI/CD platform with procedures and git integration the way Komodo is. For a home lab running one or two hosts, that’s arguably simpler and easier to wrap your head around - though to be fair, it does have some CI/CD pipeline capability too.

It’s effectively a competitor to Dockhand, but with one big difference. I mentioned Arcane’s license up top for a reason. Dockhand, despite being today’s most direct rival, ships under the commercial BSL 1.1 license, and some features sit behind a paywall even in the “free” tier.

What you’ll need before installing
#

  • A host with Docker and Docker Compose installed.
  • Port 3552 open (the default) or a reverse proxy already configured.
  • A couple of generated secrets - ENCRYPTION_KEY and JWT_SECRET (we’ll generate these in step 2).

Step 1. Write your compose.yaml
#

Create a dedicated folder for Arcane and drop a compose.yaml inside it:

services:
  arcane:  # Service name (used as the container name on the Docker network)
    image: ghcr.io/getarcaneapp/manager:latest  # Container image, tag latest - the newest version
    container_name: arcane  # Explicit container name (instead of an auto-generated one)
    #ports:
    #  - '3552:3552'  # Port publishing disabled - access goes only through Traefik, the port isn't opened directly externally
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock  # Access to the host's Docker socket - needed by the app to manage containers/images
      - /home/stilicho/docker/arcane/data:/app/data  # Persistent app data storage on the host
    environment:
      - APP_URL=https://arcane.stilicho.ru  # Public URL the app is accessible at
      - PUID=1000  # User ID inside the container (for file permissions on the volume)
      - PGID=1000  # Group ID inside the container (for file permissions on the volume)
      - ENCRYPTION_KEY=xxxxxxxxxxxxxxxxxxxxxx  # App data encryption key (secret)
      - JWT_SECRET=xxxxxxxxxxxxxxxxxxxxxx  # Secret used to sign authorization JWT tokens
      - TZ=Europe/Moscow  # Container timezone
    cgroup: host  # Container uses the host's cgroup (needed for monitoring/managing other containers' resources)
    restart: unless-stopped  # Auto-restart the container on crash/host reboot, except after a manual stop
    networks:
      proxy:  # Connect to the proxy network (used by Traefik)
    healthcheck:
      test: ['CMD', '/app/arcane', 'health']  # Command to check the container's status
      interval: 30s  # Interval between checks
      timeout: 5s  # Maximum time to wait for a response to the check
      retries: 3  # Number of failed attempts before the status becomes "unhealthy"
    labels:
      - "traefik.enable=true"  # Enable Traefik for this container
      # =========================
      # HTTP ROUTER (port 80)
      # =========================
      - "traefik.http.routers.arcane.entrypoints=web"
      # Traefik listens for incoming traffic on the "web" entrypoint (usually :80)
      # this is where http://arcane.stilicho.ru lands
      - "traefik.http.routers.arcane.rule=Host(`arcane.stilicho.ru`)"
      # Rule: if Host matches - use this router
      # Traefik compares the Host header
      - "traefik.http.routers.arcane.middlewares=arcane-https-redirect"
      # Apply the middleware (redirect to HTTPS)
      # BEFORE proxying into the container
      - "traefik.http.middlewares.arcane-https-redirect.redirectscheme.scheme=https"
      # The middleware itself:
      # Traefik does NOT send the request to the container
      # it immediately responds to the client:
      # 301 Redirect → https://arcane.stilicho.ru
      # =========================
      # HTTPS ROUTER (port 443)
      # =========================
      - "traefik.http.routers.arcane-secure.entrypoints=websecure"
      # Incoming HTTPS traffic (usually port 443)
      - "traefik.http.routers.arcane-secure.rule=Host(`arcane.stilicho.ru`)"
      # Same domain rule
      - "traefik.http.routers.arcane-secure.tls=true"
      # Enable TLS:
      # Traefik terminates SSL (TLS termination)
      # decrypts HTTPS → works as plain HTTP from here on
      - "traefik.http.routers.arcane-secure.service=arcane"
      # Specify which service to send traffic to
      # router → service pairing
      # =========================
      # SERVICE (where the traffic goes)
      # =========================
      - "traefik.http.services.arcane.loadbalancer.server.port=3552"
      # Key line:
      # Traefik takes the container's IP on the proxy network
      # and makes a request to:
      # http://arcane:3552 (inside the Docker network)
      # NOT via localhost and NOT via ports
      # =========================
      # NETWORK
      # =========================
      - "traefik.docker.network=proxy"
      # Specify which network to look for the container on
      # important if the container is on multiple networks
      # Traefik will take the IP specifically from the proxy network
# Top-level network definitions (available to all services)
networks:
  proxy:  # Name of the network the service connects to
    external: true  # The network already exists (created separately, e.g. for Traefik), Compose doesn't create it

A few things worth flagging right away:

  • PUID/PGID - leave these out and Arcane writes files as its built-in unprivileged user, 65532:65532. If you want files on the host owned by a specific account, set these to that user’s UID/GID. They’re not just decoration in the compose file.
  • /var/run/docker.sock - a required mount; it’s how Arcane actually talks to Docker. I’ll cover a safer setup using a socket proxy below.
  • arcane-data - the stock compose file suggests a Docker volume for the database and project data, but I go with a bind mount instead. That’s just my own preference.

You can optionally throw in two more volumes if you plan on using the features they unlock:

  • /builds - the working directory for the Build Workspace (building images straight from a Dockerfile inside the UI).
  • /backups - where Arcane drops exported volume backups. Yes, it can back up your Docker volumes too, so it’s worth thinking about which setup fits what you need.

Step 2. Generate your secrets
#

ENCRYPTION_KEY and JWT_SECRET both need to be 32-byte values (hex, base64, or raw). The easiest way to generate them is straight from the terminal with openssl:

echo "ENCRYPTION_KEY=$(openssl rand -hex 32)"
echo "JWT_SECRET=$(openssl rand -hex 32)"

Drop the resulting values into compose.yaml in place of xxxxxxxxxxxxxxxxxxxxxx.

Step 3. Connecting existing compose projects
#

This one’s easy to overlook. For Arcane to manage a compose project you’ve already got sitting on the host - your stacks in /opt/docker or wherever - the path to that project has to match on both sides of the container boundary. In other words, this is the wrong way to mount it:

volumes:
  - /opt/docker:/app/data/projects

The right way is a one-to-one mount plus an explicit environment variable:

volumes:
  - /opt/docker:/opt/docker
environment:
  - PROJECTS_DIRECTORY=/opt/docker

In the video I actually skipped the one-to-one mapping, and it still worked fine.

Do it this way, though, and relative paths inside your compose files (things like ./config) resolve exactly as they would if you ran docker compose by hand from that same folder.

Step 4. Running SELinux? Read this
#

If your hosts are on Fedora/RHEL/CentOS with SELinux turned on, you’ve got two options:

Option A - a socket proxy (the one I’d recommend). Instead of mounting docker.sock straight into Arcane, run tecnativa/docker-socket-proxy in front of it. It only exposes the operations you explicitly allow - in the example below that’s containers, images, networks, volumes, exec, and events, with no access to secrets, swarm, or system-level operations:

services:
  docker-socket-proxy:
    image: tecnativa/docker-socket-proxy:latest
    container_name: arcane-docker-proxy
    privileged: true
    environment:
      - EVENTS=1
      - PING=1
      - VERSION=1
      - AUTH=0
      - SECRETS=0
      - POST=1
      - BUILD=0
      - COMMIT=0
      - CONFIGS=0
      - CONTAINERS=1
      - DISTRIBUTION=0
      - EXEC=1
      - IMAGES=1
      - INFO=1
      - NETWORKS=1
      - NODES=0
      - PLUGINS=0
      - SERVICES=0
      - SESSION=0
      - SWARM=0
      - SYSTEM=0
      - TASKS=0
      - VOLUMES=1
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro

  arcane:
    image: ghcr.io/getarcaneapp/manager:latest
    container_name: arcane
    ports:
      - '3552:3552'
    volumes:
      - arcane-data:/app/data
      - /path/to/projects:/app/data/projects:z
    environment:
      - PUID=1000
      - PGID=1000
      - ENCRYPTION_KEY=xxxxxxxxxxxxxxxxxxxxxx
      - JWT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxx
      - DOCKER_HOST=tcp://docker-socket-proxy:2375

volumes:
  arcane-data:

Honestly, this is worth doing even without SELinux in the picture - it’s just good practice, since direct access to docker.sock is basically root on the host. Plenty of people are adopting this pattern now specifically to keep the Docker socket locked down on Debian/Ubuntu boxes too.

Option B - mount the socket directly, with an SELinux label via security_opt: - label:disable and the :z suffix on the projects volume. Faster to set up, but noticeably looser on security.

Step 5. Fire it up
#

docker compose up -d

Confirm the container’s actually up and not stuck restarting in a loop:

docker compose logs -f arcane

Or check the logs through Dozzle - I’ve got a full overview of that on my channel.

Step 6. Log in for the first time
#

Open http://<host-address>:3552 in your browser. On first login, Arcane will make you change the default admin password - do it immediately, before you even think about exposing this to an outside network.

Step 7. Reverse proxy and WebSocket
#

Arcane leans heavily on a WebSocket connection for real-time logs, metrics, and container status, so if you’re putting it behind a reverse proxy, you need connection-upgrade proxying enabled. If you’re already running Traefik, the labels above cover it.

Traefik proxies WebSocket out of the box - no extra config needed. Nginx or Apache will need the Upgrade/Connection headers forwarded explicitly, so keep that in mind if you’re publishing Arcane through something other than Traefik.

Step 8. Health check (optional)
#

The image ships with a built-in arcane health command, which slots nicely into a Docker healthcheck:

healthcheck:
  test: ['CMD', './arcane', 'health', '--timeout', '2s']
  interval: 10s
  timeout: 3s
  retries: 5
  start_period: 15s

start_period buys enough time for the database migrations to finish on first boot, so they don’t count against you as failed checks.

What else Arcane brings to the table
#

Beyond the basics of managing containers, images, networks, and volumes, there’s a handful of things that make this project worth a proper look, especially if you’re eyeing a move off Portainer:

  • Remote Environments - hook up remote Docker hosts through a lightweight agent, similar to Periphery in Komodo, minus the git repo and procedures - it’s just another host added to your list of environments.
  • Templates and template registries - deploy common stacks without hand-writing a compose file from scratch.
  • Vulnerability Scans - scan images for known vulnerabilities right from the UI.
  • RBAC and OIDC SSO - already running Authentik or another OIDC provider? Wire Arcane into it for single sign-on, same as you’d do with any other service in your home lab.
  • Docker Swarm - basic support, for the handful of you still running a Swarm cluster.
  • Auto-updates - Arcane can check for and pull its own updates on its own.

Wrapping up
#

Arcane is a solid pick if Portainer feels like overkill and Dockge feels too locked into bare docker-compose without proper image and volume management (not to mention the project’s basically stalled). On top of that, you get features Dockhand charges extra for. Getting it running with my compose file takes about 30 seconds - the settings actually worth thinking through ahead of time are generating real secrets, mapping out the path to your existing projects (PROJECTS_DIRECTORY), and, wherever you can, putting a socket proxy between Arcane and the Docker socket instead of mounting it directly.

Docker UI Panels - This article is part of a series.
Part : This Article

Related