# Docker Folder Setup on Homelab (with Scripts)

# Why Docker?
Docker is currently the best tool that I've found for the job of hosting linux containers.  It creates a repeatable system that is easy to deploy.  
These are the steps that the system uses to deploy the software:
- A "Dockerfile"
- - Instructions to run on the system(such as installing packages or manipulating files)
- - Ports to expose
- - Variables and labels to apply to the container image

When you build a Dockerfile it will execute all of the steps and save them as "image layers" that are saved to the host server.  If you need to change a single step of the Dockerfile then it will only need to rebuild the layers from that point onwards

There are a large number of tutorials and posts on the actual setting up of Docker or the images, but this post will go more into how I am managing multiple containers on a single host as part of a homelab

# Requirements
I am using the following software on the host:
- Docker
- Docker-compose
- [Traefik](https://blog.abowden.net/setting-up-traefik-with-ssl-certificates-for-a-homelab) (used as a web proxy for Docker services)
- Git (tracks changes to the code base and Docker images)

# Template folder
Inside of my main Docker folder I have a single folder for each service:
![Folder_Structure.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1648539684865/Ds6pWjtbG.png)
This is done because each service has a single `docker-compose.yml` file that would overwrite each other, and also for simplicity of separating files belonging to each image

When I am setting up a new service, I have a template folder that is copied from the main Docker folder with 4 files inside:
![Template_Folder_Structure.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1648539851246/7UlVULa4P.png)
### `Compose.sh`
By keeping each image in a folder that is named the same as the image name, you can use the `$PWD` command to fetch the image name based on the folder that you are currently in.
This script will stop all instances of only the current image and then restart it
```
docker stop $(docker ps -aq --filter "label=$(basename "$PWD")")
docker rm $(docker ps -aq --filter "label=$(basename "$PWD")")

docker-compose up --build -d
```

### `Debug.sh`
This script will allow you to enter the terminal of the image (based on the current folder) in order to troubleshoot
```
docker exec -it $(docker ps --filter "name=$(basename "$PWD")" -lq) /bin/bash
```

### `Stop.sh`
This script will stop all instances of only the current image (avoiding disrupting other services)
```
docker stop $(docker ps -aq --filter "label=$(basename "$PWD")")
docker rm $(docker ps -aq --filter "label=$(basename "$PWD")")
```

### `Docker-compose.yml`
This is the template for Docker images, and contains some labels that are used for the  Traefik proxy.  Most of these values will be replaced with what the image requires
```
version: '3.3'
services:
  service-name:
    image: image/image
    container_name: template
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Australia/Perth
    labels:
      - template
      - "traefik.enable=true"
      - "traefik.http.routers.template.rule=Host(`template.domain`)"
      - "traefik.http.routers.template.entrypoints=websecure"
      - "traefik.http.routers.template.tls.certresolver=myresolver"
      - "traefik.http.services.template.loadbalancer.server.port=8080"
      - "traefik.frontend.rule=Host:traefik.domain"
      - "traefik.http.routers.template.middlewares=middlewares-rate-limit@file,middlewares-ipwhilelist@file" 
    volumes:
      - /dev/null:/path/on/container
    ports:
      - "8085:80"
    restart: unless-stopped
    networks:
      web:

networks:
  web:
    external: true
```

# Scripts
I also regularly use the following scripts to tear down, update and rebuild the Docker images as required:
### `Stop_all.sh`
This script will stop all running containers
```
docker rm -f $(docker ps -a -q)
```

### `Update_images.sh`
This script will pull the latest image for each of the images specified in the array (useful for containers that are pinned to the :latest tag)
```
# List of images to pull for update
declare -a arr=("coredns/coredns:latest" "ghcr.io/linuxserver/deluge" "linuxserver/plex" "pihole/pihole")

# Loop through array
for i in "${arr[@]}"
do
    echo "--------------------------------------------------------------------------------------------------------------------------"
    echo "Updating image: $i"
    echo "--------------------------------------------------------------------------------------------------------------------------"
    docker image pull $i
done
```
### `Restart_all.sh`
This script will start up all required containers in a specific order: (useful for a new host or if you have stopped all containers)
It will cd into each folder and run the `compose.sh` script
```
# Stop all running
docker rm -f $(docker ps -a -q)

# List of services to start in order
declare -a arr=("coredns" "pihole" "traefik" "plex" "deluge")

# Loop through array
for i in "${arr[@]}"
do
    echo "--------------------------------------------------------------------------------------------------------------------------"
    echo "Starting $i"
    echo "--------------------------------------------------------------------------------------------------------------------------"
    cd $i
    ./compose.sh
    cd ..
done

# List containers
docker ps
```

# Final Thoughts
The way that this is setup with all of the code backed up to a GIT repository makes it very easy to tear down this setup and rebuild on a new host (useful for data recovery).
I have quite a few of the images dependent on each other (e.g. all services behind Traefik) so it's been much easier to manage multiple images this way
