Docker and Companion Hub
Companion Hub is a management layer on top of Docker. Understanding a few Docker concepts helps you troubleshoot issues, back up data, and know what happens when you install or remove apps.
Core concepts
Image
An image is a read-only template โ a snapshot of an application and its dependencies. When you install Nextcloud from the App Store, Hub pulls the nextcloud:latest image from a registry (usually Docker Hub or GHCR). Images are cached on disk and can be reused by many containers.
Container
A container is a running instance of an image. Each app you install gets one or more containers. When you click Stop on an app card, Hub stops its container(s) but keeps the image and your data.
Volume
A volume (or bind mount) is persistent storage attached to a container. App databases, uploads, and configuration live here โ typically under your Hub data directory (ROOT_FOLDER_HOST), and some apps use Docker-managed named volumes instead. Stopping or removing a container does not delete volumes unless you explicitly choose to remove them.
Compose
Docker Compose defines how multiple containers work together (networks, ports, dependencies). Each App Store entry ships a docker-compose.json (or legacy .yml) that Hub renders into a compose project per app.
How the pieces relate
One image can back many containers, and each container keeps its data in volumes that outlive it โ stopping or removing a container leaves both the image and the volume in place:
flowchart TD
R["Registry<br/>Docker Hub or GHCR"] -->|"pull"| I["Image<br/>read-only template, cached on disk"]
K["docker-compose.json<br/>from the App Store entry"] -->|"rendered into one<br/>compose project per app"| C
I -->|"run: one or more per app"| C["Container<br/>running instance"]
C -->|"reads and writes"| V["Volume<br/>typically under ROOT_FOLDER_HOST"]The four terms at a glance
| Term | What it is | Where it lives | When it goes away |
|---|---|---|---|
| Image | Read-only template of an app and its dependencies; reused by many containers | Image cache on disk, pulled from Docker Hub or GHCR | Only when you prune: docker image prune, or docker system prune -a for all unused images |
| Container | A running instance of an image; one or more per installed app | Managed by Hub as a compose project per app | Stop stops it but keeps the image and your data; uninstall stops it too |
| Volume | Persistent storage: databases, uploads, configuration | Typically under your Hub data directory (ROOT_FOLDER_HOST) | Only if you explicitly choose to remove it |
| Compose | Defines how containers work together: networks, ports, dependencies | docker-compose.json (or legacy .yml) shipped with each App Store entry |
What Hub manages for you
When you install an app, Hub:
- Pulls the required Docker images
- Generates an
app.envfile from your form inputs and Hub settings - Publishes the appโs port for local access and, when you expose the app publicly, adds Traefik routing for its public address. The appโs page lists each address โ see Finding your appโs URL
- Starts the compose stack and tracks status via Server-Sent Events
When you uninstall, Hub removes the appโs containers, images, and installed files, and the dialog shows one switch, Delete all data, which is on by default. With it on, Hub also deletes the appโs data folder, Docker volumes, and backups. With it off, all three stay, and the dialog says This appโs data and backups stay on this Hub.
Inspecting Docker state
Use the Hub dashboard (Settings โ System) or the CLI:
cihub app list # every Docker container on the host
cihub app status # color-coded status + ports
cihub app inspect <name> # ports, env vars, mounts
cihub status # Hub stack health + networkingDirect Docker commands also work:
docker ps # running containers
docker compose ls # compose projects
docker system df # disk usage by images/volumesCleaning up Docker state
Over time, unused images and stopped containers consume disk space.
Safe cleanup (unused resources only)
docker system prune # stopped containers, unused networks
docker image prune # dangling images only
docker system df # check usage firstAggressive cleanup (re-downloads images later)
docker system prune -a # all unused imagesdocker system prune -a removes cached images. Apps re-pull on next start,
which can take time and bandwidth. Never add --volumes: it deletes named
volumes, including ci_hub_pgdata, which holds Hubโs own database.
Full Hub reset
To return Hub to a first-time state, use one of these. Each one deletes your operators, installed apps, registration, and local app data.
- Settings โ System โ Factory reset Hub. You confirm by typing the deviceโs name. Use this while Hub is running.
cihub reset --yesfrom a terminal, for when the Hub API is down. Runcihub reset --dry-runfirst to see the apps, containers, and folders it would remove. Without--yes, the CLI asks for confirmation.
A reset is destructive and permanent. Back up important app data first. See Backup and restore.
Removing Cloudflare tunnel credentials
If you unpair from your CI Account or need to reset remote access, Hub clears the tunnel token during registration reset. You can also remove it manually:
- In the Hub dashboard: Settings โ Network โ This Hub in your account โ Reset this Hub only. This clears the Hubโs pairing. The Portal keeps the device, its web addresses, and its apps until an owner or admin deletes them there.
- On disk: delete the token file in the
tunnelfolder next to your data root, for example~/.local/share/tunnel/tokenon Linux. See Folder structure. - Restart the Hub stack:
cihub down && cihub up prod --detached
Hub starts the cloudflared container only for a paired Hub, when the tunnel folder holds both a token and a registration.json file. After you remove the credentials, the container stays stopped until you pair again and your CI Account provisions a new tunnel.
Hub stack containers
The Hub itself runs as a Docker Compose project. Typical containers include:
| Container | Role |
|---|---|
ci-hub | Backend API + dashboard (NestJS + React) |
ci-hub-db | PostgreSQL database |
ci-hub-queue | RabbitMQ message queue |
traefik | Reverse proxy and TLS |
hub-tailscale | Tailscale sidecar. It runs only when you set an auth key or the sidecar holds a saved Tailscale login; cihub also keeps it off when PRIVATE_VPN_USER_DISABLED=true |
cloudflared | Cloudflare tunnel. It runs only when Hub is paired with your CI Account |
App containers are separate compose projects managed alongside the Hub stack. Older stacks used the names ci-os-hub and ci-os-hub-queue, which still resolve as network aliases.
Leaving without lock-in
Every app runs as standard Docker Compose. If you ever stop using Hub, your containers and volumes remain on disk. Each appโs rendered compose file is apps/<store>/<app>/docker-compose.yml and its environment is app-data/<store>/<app>/app.env, both under your data root. You can start an app manually by running docker compose --env-file <app.env> -f <docker-compose.yml> up -d.