> ## Documentation Index
> Fetch the complete documentation index at: https://qovery-gdubroeucq-qov-2345.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Image Mirroring

> Optimize deployments with local image caching

When Qovery manages applications on your systems, it stores all generated container images in a registry on your cloud infrastructure. This feature is called image mirroring.

<img src="https://mintcdn.com/qovery-gdubroeucq-qov-2345/yQ_LoJqoPeddoLt3/images/deployment/mirror-registry.png?fit=max&auto=format&n=yQ_LoJqoPeddoLt3&q=85&s=7992ab4c3cc6389bae23f909e143434c" alt="Mirror Registry" width="3164" height="2070" data-path="images/deployment/mirror-registry.png" />

## Why Image Mirroring?

Image mirroring serves two main purposes:

1. **Speed up deployments**: By caching built images, Qovery avoids rebuilding identical images, significantly accelerating the deployment process
2. **Secure scale-up operations**: When Kubernetes needs to scale up your application and create additional instances, having images stored locally ensures the operation succeeds even if the third-party registry is unavailable

## How It Works

The mirroring registry is accessible and configurable through the Qovery interface under the "Mirroring registry" section of your cluster.

### For Applications Built by Qovery CI

When Qovery builds your application from Git:

<img src="https://mintcdn.com/qovery-gdubroeucq-qov-2345/yQ_LoJqoPeddoLt3/images/deployment/build-mirror.png?fit=max&auto=format&n=yQ_LoJqoPeddoLt3&q=85&s=3f8f92e611ca814527abeb6d55aabc2a" alt="Build and Mirror" width="3629" height="1330" data-path="images/deployment/build-mirror.png" />

1. After building, Qovery CI pushes the generated image to the mirroring registry
2. Images are organized by Git repository with naming: `z<short_cluster_id>-git_repo_name`
3. Qovery computes an image tag from the build inputs
4. Before cloning and again after reading the Dockerfile, Qovery checks whether that exact tag already exists
5. If the tag exists and the build is not forced, the Docker build is skipped and the existing image is used
6. Unused images are automatically deleted when no longer referenced
7. Remote cache functionality (AWS, GCP, Scaleway) rebuilds only changed layers

### When Is the Docker Image Rebuilt?

Qovery reuses an image only when the generated tag already exists in the target
registry. The tag is based on the inputs that can change the image:

* Git commit ID and repository root path
* Dockerfile path and content
* Values of build arguments declared by the Dockerfile
* Values of build secrets referenced by `RUN --mount=type=secret,id=...`
* Target build stage
* Injected files, including their paths and contents
* Inline Dockerfile fragments, or the path of a file-based fragment
* Whether Git submodules are skipped

Changing any of these inputs produces a different tag and causes a new image
to be built when that tag is not already present. Variables that are not
declared as Dockerfile `ARG` values or secret mount IDs do not affect the final
tag.

The engine performs two image-existence checks. The first uses the inputs
available before the repository is cloned. The second parses the Dockerfile,
keeps only the arguments and secrets it actually declares, and computes the
final tag. An incomplete or incorrect `tag_build_args` selection can therefore
cause the first check to miss an image even when the final tag is reusable.

This image-reuse check is different from Docker layer caching. Disabling
`build.disable_buildkit_cache` disables registry layer-cache import/export; it
does not force a rebuild when the generated image tag already exists. Secret
values change the image tag, but BuildKit may still reuse layers from a step
that consumes a secret because secret values are not part of BuildKit's layer
cache key.

`force_build` bypasses the initial check, but the post-Dockerfile check can
still skip the build if the final tag exists. The lookup is performed for the
generated tag, not for `latest`.

### For Applications from Third-Party Registries

The behavior depends on the configured mirroring mode:

#### Service Mode (Default)

Each service maintains its own mirroring repository.

<img src="https://mintcdn.com/qovery-gdubroeucq-qov-2345/yQ_LoJqoPeddoLt3/images/deployment/image-mirror-service.png?fit=max&auto=format&n=yQ_LoJqoPeddoLt3&q=85&s=debd0830e1cc54b4aef17fe642910745" alt="Service Mode" width="3629" height="1347" data-path="images/deployment/image-mirror-service.png" />

**Characteristics:**

* Images are organized per Qovery service (isolated processes)
* Automatic cleanup when images become unnecessary
* Downside: Identical images across services are mirrored multiple times

#### Cluster Mode

All applications on the same cluster share one mirroring repository.

<img src="https://mintcdn.com/qovery-gdubroeucq-qov-2345/yQ_LoJqoPeddoLt3/images/deployment/image-mirror-cluster.png?fit=max&auto=format&n=yQ_LoJqoPeddoLt3&q=85&s=0cf295d9b2e2ffc9465a4c7e2d68199b" alt="Cluster Mode" width="3629" height="1347" data-path="images/deployment/image-mirror-cluster.png" />

**Characteristics:**

* Reduces duplication for shared images across environments
* Requires manual image management via TTL settings
* Not available on Scaleway

## Important: Use Unique Image Tags

<Warning>
  Image tags must be unique. Both Qovery's mirroring system and Kubernetes employ caching mechanisms. Using the same tag for different image versions prevents new versions from deploying, causing applications to run outdated code.
</Warning>

Using tags like `latest` or other mutable tags can cause serious issues because the cached image in the mirroring registry may not be updated when you push a new version with the same tag.

**Best practices:**

* Use specific version tags: `myapp:v1.2.3`, `myapp:commit-abc123`
* Use immutable digests: `myapp@sha256:abc123...`
* Avoid mutable tags like `latest`, `stable`, or `prod`

## Disabling Mirroring

### Push Images to the Mirroring Registry

To skip mirroring operations, you can push your built images directly into the mirroring registry within repositories that match your image names.

Alternatively, if the source registry URL matches the cluster registry URL, mirroring is automatically skipped.

**Example for AWS:**

For an image `nginx` with cluster registry `https://32432542.dkr.ecr.eu-west-3.amazonaws.com`:

Push to: `https://32432542.dkr.ecr.eu-west-3.amazonaws.com/nginx`

<a id="disable-mirroring-on-scaleway-kapsule" />

### Disable Mirroring on Scaleway Kapsule

On Scaleway Kapsule clusters, you can turn mirroring off so that pods pull images directly from the registry they are stored in. Images are no longer copied into the cluster's Scaleway Container Registry, and deployments no longer depend on it being available.

This applies to **containers** and **jobs** that deploy an image from a container registry. Applications and jobs built from Git are not affected: Qovery keeps pushing the images it builds to the cluster's Scaleway Container Registry.

#### Requirements

* A **Scaleway Kapsule** cluster managed by Qovery. The setting is rejected on other cluster types.
* The image's registry is declared in your organization's [container registries](/configuration/organization/container-registry). For a private registry, its credentials must be able to **read** the image; public registries work without credentials. Qovery only pulls from the registry; it never pushes to it or deletes from it. For GitHub Container Registry, a token with the `read:packages` scope is enough (see [GitHub Container Registry](/configuration/integrations/container-registries/github-cr)).
* The registry uses long-lived credentials and an `https://` URL without a path. The table below lists which registries are pulled directly.

| Registry | With mirroring disabled |
| - | - |
| GitHub Container Registry, GitHub Enterprise, GitLab | Pulled directly |
| Generic registry | Pulled directly when its URL is `https://` without a path |
| Docker Hub with your own credentials | Pulled directly |
| Docker Hub without credentials | Still mirrored when Qovery pulls it with its own Docker Hub account; otherwise pulled directly and anonymously |
| Scaleway Container Registry, DigitalOcean Container Registry | Pulled directly. A Scaleway registry on the same host as the cluster registry (same region) is pulled with the cluster registry's credentials, as before |
| Amazon ECR Public | Pulled directly (anonymous) |
| Amazon ECR (private), Azure Container Registry, GCP Artifact Registry | Still mirrored: their credentials are temporary, or their URL contains a path |

Images that are still mirrored follow the [Service Mode](#service-mode-default) behavior, and the deployment logs say why.

#### Steps

<Steps>
  <Step title="Declare the registry in your organization">
    Add the registry your images are stored in to your organization, with credentials that can read the images. See [Container Registry](/configuration/organization/container-registry). Skip this step if your services already deploy from it.
  </Step>

  <Step title="Set the mirroring mode to Disabled">
    In the cluster's **Settings** > **Advanced Settings**, set [`registry.mirroring_mode`](/configuration/cluster-advanced-settings#registry-mirroring-mode) to `Disabled` and save.

    With Terraform, set it in `advanced_settings_json` on the `qovery_cluster` resource:

    ```hcl theme={null}
    advanced_settings_json = jsonencode({
      "registry.mirroring_mode" = "Disabled"
    })
    ```
  </Step>

  <Step title="Redeploy your services">
    Each container and job switches on its next deployment. Redeploying the cluster is not needed for this setting. Running pods keep their current image until their service is redeployed.
  </Step>
</Steps>

#### What Changes

* Pods run the image from its original registry (for example `ghcr.io/my-org/my-app:1.2.3`), with a pull secret built from the credentials of that registry in Qovery.
* Before starting the pods, the deployment logs in to the registry and checks that the image exists:
  * Invalid credentials fail the deployment with `Failed to login to registry`.
  * A missing tag, or an image the credentials cannot read, fails the deployment with `SOURCE_IMAGE_NOT_FOUND`.
  * If the registry cannot be reached at that moment (network error, rate limit, outage), the deployment continues with a warning and Kubernetes retries the pull.
* The deployment logs show `Skipping image mirroring: mirroring is disabled on the cluster, pods pull <image> directly`.

#### Things to Know

<Warning>
  Your pods depend on the source registry being available whenever Kubernetes pulls an image: on deployment, on restart, when scaling up, and when a node is replaced. If a tag is deleted from the source registry, pods using it can no longer restart.
</Warning>

* **Docker Hub rate limits:** each node pulls from Docker Hub with the registry's credentials, and anonymous pulls are limited per IP address. Add your Docker Hub credentials to the registry to pull directly with your own account. Without them, images are mirrored with Qovery's Docker Hub account when one is available, and pulled anonymously otherwise.
* **Unique image tags** remain required. See [Use Unique Image Tags](#important-use-unique-image-tags).
* **Switching back:** set `registry.mirroring_mode` to `Service` and redeploy your services. The `qovery-mirror-*` repositories stay in the Scaleway Container Registry, but a service's previously mirrored tag is deleted on its first deployment with mirroring disabled, so switching back mirrors the images again.

## Image Retention Policy

Configurable TTL (Time To Live) settings control image retention duration via the advanced setting `image_retention_time`.

<Note>
  Modifications to the retention policy only affect newly created repositories. Existing repositories remain unchanged.
</Note>

## Next Steps

<CardGroup cols={2}>
  <Card title="Deployment Pipeline" icon="diagram-project" href="/configuration/deployment/pipeline">
    Configure deployment stages
  </Card>

  <Card title="Deployment Strategies" icon="chess" href="/configuration/deployment/strategies">
    Choose deployment strategy
  </Card>

  <Card title="Container Registry" icon="box" href="/configuration/organization/container-registry">
    Configure container registries
  </Card>

  <Card title="Deployment Actions" icon="gears" href="/configuration/deployment/actions">
    Manage deployment lifecycle
  </Card>
</CardGroup>
