Skip to content
Containly

Install Containly

Install Containly with Docker Compose or docker run, create the first admin with the setup token and put it behind a reverse proxy.

Containly runs as one container on a Linux host with Docker Engine and Docker Compose v2. The image is published as amslertec/containly for linux/amd64 and linux/arm64, so a mini PC, a virtual machine, a VPS or a Raspberry Pi 4 or 5 with a 64-bit system all work. It listens on port 8420.

Containly talks to the Docker socket, which is equivalent to root on the host. Treat access to it like SSH access and read the security notes before you make it reachable from the internet.

Download the example files

Create a folder and download the example Compose file and the example environment file straight to their final names:

mkdir containly && cd containly
curl -fsSL https://raw.githubusercontent.com/amslertec/containly/main/docker-compose.example.yml -o docker-compose.yml
curl -fsSL https://raw.githubusercontent.com/amslertec/containly/main/.env.example -o .env

All app settings live in .env; the Compose file loads it with env_file.

Check two settings

  • In .env, keep CONTAINLY_SECURE_COOKIES=false as long as you open Containly over plain HTTP, for example http://<server-ip>:8420. Set it to true only behind HTTPS. With true over plain HTTP the page stays blank or black.
  • In docker-compose.yml, replace /path/to/your/compose-projects on both sides of the volume line with the folder that holds your Compose projects. The path inside the container must be the same as on the host, so the stack paths resolve.

The other values in .env can stay as they are: PORT=8420, CONTAINLY_TRUST_PROXY=true, LOG_LEVEL=info. The session secret is created on the first start and kept in the data volume.

Start with Docker Compose

docker compose pull
docker compose up -d
docker compose logs -f containly

The data lives in two named volumes: containly-data (database, session secret, master key) and containly-stacks (stacks created in Containly). They survive updates and re-creates.

Or start with docker run

If you prefer a single command, this is the same setup as the example Compose file (run it in the folder with your .env):

docker run -d --name containly --restart unless-stopped \
  --security-opt no-new-privileges:true --cap-drop ALL \
  --env-file .env \
  -v containly-data:/data \
  -v containly-stacks:/stacks \
  -v /path/to/your/compose-projects:/path/to/your/compose-projects \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  -p 8420:8420 \
  amslertec/containly:latest
docker logs -f containly

The Compose file is the documented way; the optional socket proxy below needs it.

Create the first admin

On the first start there is no admin account. Containly runs in setup mode and prints a one-time setup token to the log, inside a boxed banner. The token is also stored in the containly-data volume as /data/setup.token.

  1. Open http://<server-ip>:8420 in your browser. The setup form appears as long as no admin exists.
  2. Enter a username and a password (you can reveal it and must confirm it), and paste the setup token.
  3. Submit. The first admin is created, setup mode closes for good and the token is deleted.

Then turn on two-factor authentication for your account.

Put it behind a reverse proxy

For access from outside your own network, put a reverse proxy with TLS in front of Containly, for example Traefik, Caddy or nginx, and change these settings:

  • Bind the port to the host only: "127.0.0.1:8420:8420" under ports: in docker-compose.yml (with docker run: -p 127.0.0.1:8420:8420).
  • Set CONTAINLY_SECURE_COOKIES=true in .env.
  • Keep CONTAINLY_TRUST_PROXY=true, so Containly reads the forwarded headers.
  • Optional: CONTAINLY_PUBLIC_URL=https://containly.example.com. Notification e-mails then link to that address.
CONTAINLY_SECURE_COOKIES=true
CONTAINLY_TRUST_PROXY=true
CONTAINLY_PUBLIC_URL=https://containly.example.com

Restart with docker compose up -d after changing .env.

Optional: filtered socket proxy

For least-privilege access to Docker, the example Compose file contains a filtered socket proxy (profile hardened):

docker compose --profile hardened up -d

Then add a TCP endpoint on socket-proxy:2375 in Containly and remove the direct socket mount from the containly service. That port is only reachable on the internal Compose network; never publish plain port 2375.

Next steps