--- title: Docker description: Install Docker and Dockge on Debian to deploy and manage self-hosted services with simple container stacks. --- :ellipsis{left=0px width=40rem top=10rem blur=140px zIndex=60} Every app in this guide, [Jellyfin](/serveex/media/jellyfin), [Vaultwarden](/serveex/apps/vaultwarden), [Immich](/serveex/cloud/immich), to name a few, comes with its own list of dependencies, its own version of Python or Node, its own quirks. Installing all of that directly on Debian works for a while, until two apps want a different version of the same library, or removing one leaves files scattered across the system with no clean way back. A **container** sidesteps the problem: it packages an app together with everything it needs to run, isolated from the rest of the system and from every other container. Starting one doesn't touch Debian's own packages, and removing it is a single command that leaves nothing behind. It's not a virtual machine either, there's no second operating system to boot or resources to pre-allocate: a container shares the host's kernel and starts in about a second, using only the RAM and CPU the app inside it actually needs. **Docker** is the tool that builds, starts and manages these containers. Point it at an *image*, a ready-made snapshot of an app maintained by its developers, and it downloads it and runs it in one command. The rest of Serveex is built entirely on it: every app from here on is one Docker container, or a handful of them working together. ![picture](/img/serveex/docker.svg) ## Install Docker ::steps{level="3"} ### Add the Docker repository and GPG key ```bash [Terminal] # Add Docker's official GPG key: sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc # Add the repository to Apt sources: echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update ``` ### Install the packages ```bash [Terminal] sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin ``` ### Done! :: **More options:** [Install Docker for Debian 13](https://docs.docker.com/engine/install/debian/) ::note From here on, we assume the stacks are installed in the `/srv/docker` folder, created using the command: ```bash [Terminal] sudo mkdir /srv/docker ``` :: ## Install [Dockge](https://github.com/louislam/dockge) to manage and deploy containers [Dockge](https://github.com/louislam/dockge) is a web tool to create, configure, launch, and manage Docker containers. It's a simple, intuitive interface that’s lighter and easier for beginners than using the CLI or Portainer. ![picture](/img/serveex/dockge.png) ### Configuration ::file-tree --- label: File structure we will create tree: /: - srv: - docker: - dockge: - compose.yaml --- :: ::steps{level="4"} #### Create the stack folder ```bash [Terminal] cd /srv/docker sudo mkdir dockge ``` #### Create the compose file ```bash [Terminal] cd /srv/docker/dockge sudo nano compose.yaml ``` Paste the following: ```yaml [compose.yaml] --- services: dockge: image: louislam/dockge:1 restart: unless-stopped container_name: dockge ports: - 3555:5001 # LAN-accessible port will be 3555 environment: - DOCKER_HOST=tcp://docker-socket-proxy:2375 - DOCKGE_STACKS_DIR=/srv/docker volumes: - /srv/docker/dockge/data:/app/data - /srv/docker:/srv/docker networks: - dockge-internal depends_on: - docker-socket-proxy docker-socket-proxy: image: lscr.io/linuxserver/socket-proxy:latest container_name: docker-socket-proxy-dockge security_opt: - no-new-privileges:true networks: - dockge-internal volumes: - /var/run/docker.sock:/var/run/docker.sock:ro environment: - CONTAINERS=1 - IMAGES=1 - NETWORKS=1 - VOLUMES=1 - EXEC=1 - INFO=1 - SYSTEM=1 - POST=1 - ALLOW_START=1 - ALLOW_STOP=1 - ALLOW_RESTARTS=1 restart: unless-stopped read_only: true tmpfs: - /run networks: dockge-internal: name: dockge-internal ``` ::warning Dockge needs access to the Docker API to manage every other stack on this server, which is effectively root access to your host. Instead of mounting `/var/run/docker.sock` directly, this config sits **Docker Socket Proxy** in front of it, only allowing the specific permissions Dockge needs (containers, images, networks, volumes, exec, lifecycle actions), on their own internal network. Dockge has no built-in login by default, so never expose port `3555` beyond your LAN without putting it behind [TinyAuth](/serveex/security/tinyauth) or [Authentik](/serveex/advanced/authentik) first. :: Press :kbd{value="Ctrl+O"}, then :kbd{value="Enter"} to save, and :kbd{value="Ctrl+X"} to exit. #### Launch the container ```bash [Terminal] cd /srv/docker/dockge sudo docker compose up -d ``` Then go to `http://yourserverip:3555` in your browser to access the login page. More info on [Dockge and how to use it](https://github.com/louislam/dockge) #### Done! :: And there you go! Docker and a tool to easily manage your containers are ready! ## [Watchtower](https://watchtower.nickfedor.com/), to auto-update containers Watchtower is a container that checks for updates and pulls new images automatically, just by adding a label in your containers’ `compose.yaml` files. ### Configuration ::steps{level="4"} #### Create the stack - Open Dockge in your browser - Click `compose` - Name the stack `watchtower` - Paste the config below into the default config area in Dockge ```yaml [compose.yaml] --- services: watchtower: container_name: watchtower image: ghcr.io/nicholas-fedor/watchtower:latest restart: unless-stopped env_file: - .env environment: - TZ=Europe/Paris - WATCHTOWER_SCHEDULE=${SCHEDULE} - WATCHTOWER_LABEL_ENABLE=true - WATCHTOWER_CLEANUP=true - WATCHTOWER_REMOVE_VOLUMES=true - DOCKER_HOST=tcp://docker-socket-proxy:2375 # Discord notifications - uncomment if used #- WATCHTOWER_NOTIFICATIONS=slack #- WATCHTOWER_NOTIFICATION_SLACK_IDENTIFIER=Watchtower #- WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL=${WH_URL} networks: - watchtower-internal depends_on: - docker-socket-proxy docker-socket-proxy: image: lscr.io/linuxserver/socket-proxy:latest container_name: docker-socket-proxy-watchtower security_opt: - no-new-privileges:true networks: - watchtower-internal volumes: - /var/run/docker.sock:/var/run/docker.sock:ro environment: - CONTAINERS=1 - IMAGES=1 - NETWORKS=1 - VOLUMES=1 - INFO=1 - SYSTEM=1 - POST=1 - ALLOW_START=1 - ALLOW_STOP=1 - ALLOW_RESTARTS=1 restart: unless-stopped read_only: true tmpfs: - /run networks: watchtower-internal: name: watchtower-internal ``` ::warning `WATCHTOWER_REMOVE_VOLUMES=true` deletes a container's anonymous volumes as soon as it's updated. Combined with a `latest` tag, an automatic update can silently wipe data for any app that still stores something in an anonymous (unnamed) volume instead of a bind mount. :: ::note This config sits **Docker Socket Proxy** in front of the Docker API instead of mounting `/var/run/docker.sock` directly, so Watchtower only gets the permissions it actually needs (list/pull images, recreate containers) rather than full root-equivalent access to the host. :: #### Set your environment variables Fill in the `.env` section in Dockge with the following: ```properties [.env] SCHEDULE= WH_URL= ``` | Property | Value | Examples | |----------------|--------------------------------------------------------------------|----------------------------------------------| | `SCHEDULE` | Cron format | `0 0 6 * * *` (every day at 6 AM) | | `WH_URL` | Your Discord webhook URL - append `/slack` at the end | `https://yourdiscordserver/webhook/slack` | #### Enable Watchtower on other containers To have Watchtower monitor your other containers, add this to their `compose.yaml`: ```yaml [compose.yaml] --- services: yourapp: # ... labels: - com.centurylinklabs.watchtower.enable=true ``` Then restart the modified stacks. #### Done! :: And that's it! You now have a solid base to start deploying the services you want!