> For the complete documentation index, see [llms.txt](https://docs.helmut.de/helmut4-releases/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.helmut.de/helmut4-releases/getting-started/upgrade-guide/helmut4-server/docker-and-portainer-update.md).

# Docker & Portainer Update

## Overview

For new Helmut4 installations, the latest supported versions of Docker and Portainer CE (Community Edition) are downloaded and installed.

For more information, refer to the [Helmut4 Server](/helmut4-releases/getting-started/installation-guide/helmut4-server/helmut4-single-server.md#portainer-helmut4-docker-configuration) documentation.

## Updates

Subsequent updates or upgrades of Docker and Portainer are the responsibility of the customer or hosting provider. Helmut4 updates do not manage the installed versions of Docker or Portainer.

Before performing an update, review the relevant Docker and Portainer release notes and verify compatibility with the operating system, Helmut4, and any installed integrations.

Always test updates in a staging environment before applying them to production.

For more information, refer to:

* Docker: <https://www.docker.com/>
* Portainer: [https://www.portainer.io](https://www.portainer.io/)

### Regression Testing and Grace Period

After updating Docker or Portainer in staging, allow a defined grace period for regression testing.

During this period, verify that:

* all Helmut4 containers start and remain healthy;
* container networking and communication work as expected;
* persistent volumes and configuration data remain accessible;
* Helmut4 workflows and integrations function correctly;
* scheduled jobs, backups, monitoring, and logging continue to operate;
* Portainer can correctly display and manage the Docker environment; and
* containers can be restarted, recreated, and updated successfully where applicable.

The production update may proceed only after the staging environment has remained stable throughout the grace period, all critical workflows have passed testing, and a rollback or recovery plan has been confirmed.

If testing identifies an issue, postpone the production update, resolve the issue, and repeat the validation process. Document the test results and approval before updating production.

### Portainer Update

With the release of Docker 26, Portainer introduced a new release model. For more information, refer to the [Portainer release principle](https://www.portainer.io/blog/2024-release-principle).

* LTS (Long-Term Support) versions use the `:lts` tag.
* STS (Short-Term Support) versions use the `:sts` tag.

Select the appropriate release type according to your organization's stability and support requirements.

For more information, refer to the [Portainer website](https://www.portainer.io).

<details>

<summary>Remove and re-deploy latest Portainer CE</summary>

Please use the provided script with caution, as Portainer is not within Helmut4's scope / support.

```bash
#!/bin/bash

# Update Portainer to the latest community edition

# moovIT GmbH - B. Dimmel

# Version 3.1
# Changelog: Aug 18th 2026
# Update Portainer from v1.x to 2.x
# Can also be used to update 2.x to a newer version
# Get container ID by 'names', making it more dynamically
# Add option to pick LTS or new STS Short-Term version
# changed tag latest to lts
# fixed syntax error in docker pull command
# added check if script is run with sudo permissions
# added check if the existing container is CE or EE
# Improved error handling
# Refactored deployment process
# Refactored the stopping and purging of deprecated Portainer images + containers
# Adding repository check before purging image

portainerTargetCode="ce"
portainerTargetName="Community Edition"
portainerWrongCode="ee"
portainerWrongName="Business Edition"

# Check if the script is run with sudo permissions.
if [ "$EUID" -ne 0 ]; then
    echo "Please run this script as root (e.g., 'sudo ./update-portainer.sh')"
    exit 1
fi

# Find the running container ID for Portainer.
container_id=$(docker ps --filter "name=portainer" --format "{{.ID}}")

if [[ -z "$container_id" ]]; then
    echo "No running Portainer container found. Skipping version check."
else
    existing_image=$(docker inspect --format='{{.Config.Image}}' "$container_id")
    echo -e "\nFound running Portainer container using image: $existing_image"

    if [[ "$existing_image" == *"portainer-$portainerWrongCode"* ]]; then
        echo -e "\nYou are trying to replace Portainer $portainerWrongCode ($portainerWrongName) with $portainerTargetCode ($portainerTargetName).\nExiting.\n"
        exit 1
    fi
fi

# Prompt user for LTS, STS, or quit option.
while true; do
    echo -e "\nDo you want to update to the latest LTS (Long-Term) or STS (Short-Term Support) version?\n"
    read -r -p "Enter 'lts', 'sts', or 'quit': " version_choice

    case "$version_choice" in
        lts)
            docker_tag="portainer/portainer-$portainerTargetCode:lts"
            break
            ;;
        sts)
            docker_tag="portainer/portainer-$portainerTargetCode:sts"
            break
            ;;
        quit)
            echo -e "Portainer update aborted!\n"
            exit 0
            ;;
        *)
            echo -e "Invalid choice!\nPlease enter 'lts', 'sts', or 'quit'.\n"
            ;;
    esac
done

# Confirm before proceeding.
while true; do
    echo -e "\n==> You have chosen: $version_choice <==\n"
    read -r -p "Proceed with this version? (y/n): " confirm_choice

    case "$confirm_choice" in
        [yY]) break ;;
        [nN]) echo -e "Please choose again.\n" ;;
        *) echo "Invalid input. Please enter 'y' or 'n'." ;;
    esac
done

# Pull and verify the replacement image BEFORE stopping/removing the existing
# container or deleting any local images. If the registry is unavailable, the
# current installation remains untouched.
echo -e "\nPulling the specified Portainer-$portainerTargetCode image: $docker_tag ..."
if ! docker pull --quiet "$docker_tag" >/dev/null 2>&1; then
    echo -e "\nFailed to pull $docker_tag. The existing Portainer installation was not changed."
    exit 1
fi

if ! docker image inspect "$docker_tag" >/dev/null 2>&1; then
    echo -e "\nThe pulled image $docker_tag could not be verified. The existing Portainer installation was not changed."
    exit 1
fi
echo -e "\nPortainer image pulled and verified successfully."

# Stop and remove all existing Portainer containers.
portainer_containers=$(docker ps -a --filter "name=portainer" --format "{{.ID}}")

if [[ -n "$portainer_containers" ]]; then
    echo -e "\nFound the following Portainer containers: $portainer_containers"

    echo -e "\nStopping all running Portainer containers ...\n"
    for container_id in $portainer_containers; do
        if docker ps --filter "id=$container_id" --format "{{.ID}}" | grep -q .; then
            if docker stop "$container_id" >/dev/null 2>&1; then
                echo "Stopped Portainer container: $container_id"
            else
                echo "Failed to stop Portainer container: $container_id. Continuing ..."
            fi
        fi
    done

    echo -e "\nRemoving all Portainer containers ..."
    for container_id in $portainer_containers; do
        if docker ps -a --filter "id=$container_id" --format "{{.ID}}" | grep -q .; then
            if docker rm "$container_id" >/dev/null 2>&1; then
                echo -e "\nRemoved Portainer container: $container_id"
            else
                echo -e "\nFailed to remove Portainer container: $container_id. Exiting."
                exit 1
            fi
        fi
    done
else
    echo -e "\nNo existing Portainer containers found. Proceeding with deployment."
fi

# Ensure no stopped Portainer containers remain before deployment.
remaining_containers=$(docker ps -a --filter "name=portainer" --format "{{.ID}}")
if [[ -n "$remaining_containers" ]]; then
    echo -e "\nForce removing any remaining Portainer containers ..."
    docker rm -f $remaining_containers
fi

# Find and remove all old Portainer images. The replacement image is retained.
echo -e "\nSearching for old Portainer images to remove ..."
portainer_images=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -E "portainer/portainer(-$portainerTargetCode)?" | grep -v -F "$docker_tag" || true)

if [[ -n "$portainer_images" ]]; then
    echo -e "\nRemoving the following old Portainer images: $portainer_images"
    while IFS= read -r image; do
        if [[ -n "$image" ]]; then
            echo -e "\nRemoving image: $image"
            if docker rmi -f "$image" >/dev/null 2>&1; then
                echo -e "Removed Portainer image: $image"
            else
                echo -e "Failed to remove Portainer image: $image. Continuing ..."
            fi
        fi
    done <<< "$portainer_images"
else
    echo -e "\nNo old Portainer images found to remove."
fi

# Deploy the verified Portainer container.
echo -e "\nDeploying the new Portainer-$portainerTargetCode container ..."
if docker run -it -d --restart=always -p 9000:9000 --name=portainer \
    -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data "$docker_tag" >/dev/null 2>&1; then
    echo -e "\n\n => Portainer $portainerTargetName has been successfully deployed. <=\n\n"
else
    echo -e "\nFailed to deploy Portainer $portainerTargetName. Exiting.\n"
    exit 1
fi

```

</details>

### Docker Update

To update the Docker Engine or Docker environment, follow the documentation provided by the operating system or distribution vendor, such as Ubuntu.

Update commands vary depending on the distribution and version installed on the server.

For Ubuntu, refer to the official [Docker Engine installation guide](https://docs.docker.com/engine/install/ubuntu).

For general information, refer to the [Docker website](https://www.docker.com/).

<details>

<summary>Update Docker on Ubuntu / Debian</summary>

```sh
sudo apt update
sudo apt upgrade docker-ce
```

</details>

<details>

<summary>Update Docker on RHEL</summary>

```sh
sudo yum check-update
sudo yum update docker-ce
```

</details>
