Backup Docker Container configuration script

A while ago I looked into a good way to backup Docker configurations for each container.  This is useful if you have many containers and want a backup strategy that includes container settings.

The below script is from https://github.com/007revad/Docker_Autocompose and does rely on the image red5d/docker-autocompose which the script will download if not already present on your Docker server.

Save the following script as docker-autocompose.sh and run with the commands shown at the top.

#!/usr/bin/env sh
#--------------------------------------------------------------------------------------
# A script to create docker-compose.yml files from docker containers.
#
# Script can be run with a container name parameter to only process that container:
# sudo -s docker-autocompose.sh plex
#
# Or with no parameter, or the "all" parameter, to process all containers:
# sudo -s docker-autocompose.sh all
#
# https://github.com/007revad/Docker_Autocompose
# Adapted from: https://www.synoforum.com/threads/docker-autocompose.4644/#post-20341
#--------------------------------------------------------------------------------------
# REQUIRED:
#
# Needs Red5d/docker-autocompose installed in docker.
# Red5d/docker-autocompose should not be started in docker.
#--------------------------------------------------------------------------------------

# Set the path where you want to .yml files saved to. If blank will save in your home.
saveto="/opt/backup-docker"

# Set to yes to include hostname in the folder name.
# Handy if you have multiple devices that backup to the same location.
IncludeHostname=yes

# Set to yes to backup all containers (running or stopped), or no to backup only running containers.
BackupAllContainers=yes

#--------------------------------------------------------------------------------------

autocompose="red5d/docker-autocompose"

# Check script is running as root (or docker.sock won't run)
if [ $( whoami ) != "root" ]; then
    echo "Script needs to run as root. Aborting."
    exit 1
fi

# Check our saveto path exists (if saveto is set)
if [ ! -d "${saveto}" ]; then
    echo "saveto path not found. Files will be saved in your home."
    saveto=
fi 

# Get hostname if IncludeHostname is yes
echo "IncludeHostname is set to: '${IncludeHostname}'"  # Debugging
if [ "${IncludeHostname}" = "yes" ]; then
    host="$(hostname)_"
fi

# Create subfolder with date
if [ -d "${saveto}" ]; then
    Now=$( date '+%Y%m%d')
    if mkdir -p "${saveto}/${Now}_${host}docker-autocompose"; then
        path="${saveto}/${Now}_${host}docker-autocompose/"
    fi
fi

# Function to process a single container
process_container() {
    container_name="${1}"
    container_id=$(docker container ls -a -q --filter name="${container_name}")
    
    # Check if container exists
    if [ -z "${container_id}" ]; then
        echo "Container '${container_name}' not found. Skipping."
        return
    fi

    # Skip non-running containers if BackupAllContainers=no
    if [ "${BackupAllContainers}" != "yes" ]; then
        is_running=$(docker container inspect -f '{{.State.Running}}' "${container_id}")
        if [ "${is_running}" != "true" ]; then
            echo "Container '${container_name}' is not running. Skipping (BackupAllContainers=no)."
            return
        fi
    fi

    # Check if container is running
    is_running=$(docker container inspect -f '{{.State.Running}}' "${container_id}")
    
    if [ "${is_running}" != "true" ] && [ "${BackupAllContainers}" = "yes" ]; then
        echo "Starting container '${container_name}' temporarily..."
        if docker start "${container_id}" > /dev/null 2>&1; then
            # Generate docker-compose.yml
            docker run --rm -v /var/run/docker.sock:/var/run/docker.sock "${autocompose}" "${container_id}" > "${path}${container_name}-compose.yml"
            # Stop the container after generating the file
            docker stop "${container_id}" > /dev/null 2>&1
            echo "Generated docker-compose.yml for '${container_name}' and stopped it."
        else
            echo "Failed to start container '${container_name}'. Skipping."
            return
        fi
    else
        # Container is already running, generate docker-compose.yml directly
        docker run --rm -v /var/run/docker.sock:/var/run/docker.sock "${autocompose}" "${container_id}" > "${path}${container_name}-compose.yml"
        echo "Generated docker-compose.yml for running container '${container_name}'."
    fi
}

# Do the magic
case "${1}" in
    all|"")
        # Create a docker-compose.yml file for each container
        # Clear existing arguments
        while [ "${1}" ]; do
            shift
        done
        # Create array of container names based on BackupAllContainers setting
        if [ "${BackupAllContainers}" = "yes" ]; then
            set -- $(docker container ls -a --format '{{.Names}}')
            echo "Backing up all containers (running and stopped)."
        else
            set -- $(docker ps --format '{{.Names}}')
            echo "Backing up only running containers."
        fi
        while [ "${1}" ]; do
            process_container "${1}"
            shift
        done
        ;;
    *)
        # Only process specified container
        process_container "${1}"
        ;;
esac

echo "All done"
exit 0