Create GCP Artifact Registry Repositories

The gcp:artifactregistry/repository:Repository resource, part of the Pulumi GCP provider, defines an Artifact Registry repository: its format, location, mode, and access configuration. This guide focuses on four capabilities: regional and multi-region deployment, virtual and remote repository modes, cleanup policies and encryption, and authentication for external registries.

Repositories may reference KMS keys, Secret Manager secrets, or upstream repositories that must exist separately. The examples are intentionally small. Combine them with your own IAM policies, networking, and access controls.

Create a regional Docker repository with metadata

Most deployments start with a standard repository in a specific region, storing container images for CI/CD pipelines.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const my_repo = new gcp.artifactregistry.Repository("my-repo", {
    location: "us-central1",
    repositoryId: "my-repository",
    description: "example docker repository",
    format: "DOCKER",
});
import pulumi
import pulumi_gcp as gcp

my_repo = gcp.artifactregistry.Repository("my-repo",
    location="us-central1",
    repository_id="my-repository",
    description="example docker repository",
    format="DOCKER")
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/artifactregistry"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := artifactregistry.NewRepository(ctx, "my-repo", &artifactregistry.RepositoryArgs{
			Location:     pulumi.String("us-central1"),
			RepositoryId: pulumi.String("my-repository"),
			Description:  pulumi.String("example docker repository"),
			Format:       pulumi.String("DOCKER"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var my_repo = new Gcp.ArtifactRegistry.Repository("my-repo", new()
    {
        Location = "us-central1",
        RepositoryId = "my-repository",
        Description = "example docker repository",
        Format = "DOCKER",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.artifactregistry.Repository;
import com.pulumi.gcp.artifactregistry.RepositoryArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var my_repo = new Repository("my-repo", RepositoryArgs.builder()
            .location("us-central1")
            .repositoryId("my-repository")
            .description("example docker repository")
            .format("DOCKER")
            .build());

    }
}
resources:
  my-repo:
    type: gcp:artifactregistry:Repository
    properties:
      location: us-central1
      repositoryId: my-repository
      description: example docker repository
      format: DOCKER

The location property places the repository in a specific region. The repositoryId sets the repository name (must be unique within the location). The format property determines what artifact types the repository accepts (DOCKER, MAVEN, NPM, PYTHON, APT, YUM, or others). The description adds human-readable metadata.

Deploy a multi-region repository for global access

Teams with globally distributed infrastructure need repositories that replicate across regions to reduce latency.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const my_repo = new gcp.artifactregistry.Repository("my-repo", {
    repositoryId: "my-repository",
    description: "example docker repository",
    location: "us",
    format: "DOCKER",
});
import pulumi
import pulumi_gcp as gcp

my_repo = gcp.artifactregistry.Repository("my-repo",
    repository_id="my-repository",
    description="example docker repository",
    location="us",
    format="DOCKER")
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/artifactregistry"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := artifactregistry.NewRepository(ctx, "my-repo", &artifactregistry.RepositoryArgs{
			RepositoryId: pulumi.String("my-repository"),
			Description:  pulumi.String("example docker repository"),
			Location:     pulumi.String("us"),
			Format:       pulumi.String("DOCKER"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var my_repo = new Gcp.ArtifactRegistry.Repository("my-repo", new()
    {
        RepositoryId = "my-repository",
        Description = "example docker repository",
        Location = "us",
        Format = "DOCKER",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.artifactregistry.Repository;
import com.pulumi.gcp.artifactregistry.RepositoryArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var my_repo = new Repository("my-repo", RepositoryArgs.builder()
            .repositoryId("my-repository")
            .description("example docker repository")
            .location("us")
            .format("DOCKER")
            .build());

    }
}
resources:
  my-repo:
    type: gcp:artifactregistry:Repository
    properties:
      repositoryId: my-repository
      description: example docker repository
      location: us
      format: DOCKER

Setting location to “us”, “europe”, or “asia” creates a multi-region repository that replicates artifacts across multiple regions within that geography. This improves pull performance for globally distributed workloads but increases storage costs.

Enforce immutable tags for Docker images

Production environments often require immutable tags to prevent accidental overwrites of deployed versions.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const my_repo = new gcp.artifactregistry.Repository("my-repo", {
    location: "us-central1",
    repositoryId: "my-repository",
    description: "example docker repository",
    format: "DOCKER",
    dockerConfig: {
        immutableTags: true,
    },
});
import pulumi
import pulumi_gcp as gcp

my_repo = gcp.artifactregistry.Repository("my-repo",
    location="us-central1",
    repository_id="my-repository",
    description="example docker repository",
    format="DOCKER",
    docker_config={
        "immutable_tags": True,
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/artifactregistry"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := artifactregistry.NewRepository(ctx, "my-repo", &artifactregistry.RepositoryArgs{
			Location:     pulumi.String("us-central1"),
			RepositoryId: pulumi.String("my-repository"),
			Description:  pulumi.String("example docker repository"),
			Format:       pulumi.String("DOCKER"),
			DockerConfig: &artifactregistry.RepositoryDockerConfigArgs{
				ImmutableTags: pulumi.Bool(true),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var my_repo = new Gcp.ArtifactRegistry.Repository("my-repo", new()
    {
        Location = "us-central1",
        RepositoryId = "my-repository",
        Description = "example docker repository",
        Format = "DOCKER",
        DockerConfig = new Gcp.ArtifactRegistry.Inputs.RepositoryDockerConfigArgs
        {
            ImmutableTags = true,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.artifactregistry.Repository;
import com.pulumi.gcp.artifactregistry.RepositoryArgs;
import com.pulumi.gcp.artifactregistry.inputs.RepositoryDockerConfigArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var my_repo = new Repository("my-repo", RepositoryArgs.builder()
            .location("us-central1")
            .repositoryId("my-repository")
            .description("example docker repository")
            .format("DOCKER")
            .dockerConfig(RepositoryDockerConfigArgs.builder()
                .immutableTags(true)
                .build())
            .build());

    }
}
resources:
  my-repo:
    type: gcp:artifactregistry:Repository
    properties:
      location: us-central1
      repositoryId: my-repository
      description: example docker repository
      format: DOCKER
      dockerConfig:
        immutableTags: true

The dockerConfig block contains Docker-specific settings. Setting immutableTags to true prevents tag overwrites; once a tag is pushed, it cannot be changed or deleted. This protects production deployments from accidental image updates.

Encrypt repository contents with customer-managed keys

Organizations with strict compliance requirements need to control encryption keys rather than relying on Google-managed encryption.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const project = gcp.organizations.getProject({});
const cryptoKey = new gcp.kms.CryptoKeyIAMMember("crypto_key", {
    cryptoKeyId: "kms-key",
    role: "roles/cloudkms.cryptoKeyEncrypterDecrypter",
    member: project.then(project => `serviceAccount:service-${project.number}@gcp-sa-artifactregistry.iam.gserviceaccount.com`),
});
const my_repo = new gcp.artifactregistry.Repository("my-repo", {
    location: "us-central1",
    repositoryId: "my-repository",
    description: "example docker repository with cmek",
    format: "DOCKER",
    kmsKeyName: "kms-key",
}, {
    dependsOn: [cryptoKey],
});
import pulumi
import pulumi_gcp as gcp

project = gcp.organizations.get_project()
crypto_key = gcp.kms.CryptoKeyIAMMember("crypto_key",
    crypto_key_id="kms-key",
    role="roles/cloudkms.cryptoKeyEncrypterDecrypter",
    member=f"serviceAccount:service-{project.number}@gcp-sa-artifactregistry.iam.gserviceaccount.com")
my_repo = gcp.artifactregistry.Repository("my-repo",
    location="us-central1",
    repository_id="my-repository",
    description="example docker repository with cmek",
    format="DOCKER",
    kms_key_name="kms-key",
    opts = pulumi.ResourceOptions(depends_on=[crypto_key]))
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/artifactregistry"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/kms"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		project, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
		if err != nil {
			return err
		}
		cryptoKey, err := kms.NewCryptoKeyIAMMember(ctx, "crypto_key", &kms.CryptoKeyIAMMemberArgs{
			CryptoKeyId: pulumi.String("kms-key"),
			Role:        pulumi.String("roles/cloudkms.cryptoKeyEncrypterDecrypter"),
			Member:      pulumi.Sprintf("serviceAccount:service-%v@gcp-sa-artifactregistry.iam.gserviceaccount.com", project.Number),
		})
		if err != nil {
			return err
		}
		_, err = artifactregistry.NewRepository(ctx, "my-repo", &artifactregistry.RepositoryArgs{
			Location:     pulumi.String("us-central1"),
			RepositoryId: pulumi.String("my-repository"),
			Description:  pulumi.String("example docker repository with cmek"),
			Format:       pulumi.String("DOCKER"),
			KmsKeyName:   pulumi.String("kms-key"),
		}, pulumi.DependsOn([]pulumi.Resource{
			cryptoKey,
		}))
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var project = Gcp.Organizations.GetProject.Invoke();

    var cryptoKey = new Gcp.Kms.CryptoKeyIAMMember("crypto_key", new()
    {
        CryptoKeyId = "kms-key",
        Role = "roles/cloudkms.cryptoKeyEncrypterDecrypter",
        Member = $"serviceAccount:service-{project.Apply(getProjectResult => getProjectResult.Number)}@gcp-sa-artifactregistry.iam.gserviceaccount.com",
    });

    var my_repo = new Gcp.ArtifactRegistry.Repository("my-repo", new()
    {
        Location = "us-central1",
        RepositoryId = "my-repository",
        Description = "example docker repository with cmek",
        Format = "DOCKER",
        KmsKeyName = "kms-key",
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            cryptoKey,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import com.pulumi.gcp.kms.CryptoKeyIAMMember;
import com.pulumi.gcp.kms.CryptoKeyIAMMemberArgs;
import com.pulumi.gcp.artifactregistry.Repository;
import com.pulumi.gcp.artifactregistry.RepositoryArgs;
import com.pulumi.resources.CustomResourceOptions;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        final var project = OrganizationsFunctions.getProject(GetProjectArgs.builder()
            .build());

        var cryptoKey = new CryptoKeyIAMMember("cryptoKey", CryptoKeyIAMMemberArgs.builder()
            .cryptoKeyId("kms-key")
            .role("roles/cloudkms.cryptoKeyEncrypterDecrypter")
            .member(String.format("serviceAccount:service-%s@gcp-sa-artifactregistry.iam.gserviceaccount.com", project.number()))
            .build());

        var my_repo = new Repository("my-repo", RepositoryArgs.builder()
            .location("us-central1")
            .repositoryId("my-repository")
            .description("example docker repository with cmek")
            .format("DOCKER")
            .kmsKeyName("kms-key")
            .build(), CustomResourceOptions.builder()
                .dependsOn(cryptoKey)
                .build());

    }
}
resources:
  my-repo:
    type: gcp:artifactregistry:Repository
    properties:
      location: us-central1
      repositoryId: my-repository
      description: example docker repository with cmek
      format: DOCKER
      kmsKeyName: kms-key
    options:
      dependsOn:
        - ${cryptoKey}
  cryptoKey:
    type: gcp:kms:CryptoKeyIAMMember
    name: crypto_key
    properties:
      cryptoKeyId: kms-key
      role: roles/cloudkms.cryptoKeyEncrypterDecrypter
      member: serviceAccount:service-${project.number}@gcp-sa-artifactregistry.iam.gserviceaccount.com
variables:
  project:
    fn::invoke:
      function: gcp:organizations:getProject
      arguments: {}

The kmsKeyName property references a Cloud KMS key for encrypting repository contents. The CryptoKeyIAMMember grants the Artifact Registry service account permission to use the key. The dependsOn ensures the IAM binding exists before creating the repository.

Aggregate multiple repositories with priority routing

Virtual repositories act as a single endpoint that routes requests to multiple upstream repositories based on priority.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const my_repo_upstream_1 = new gcp.artifactregistry.Repository("my-repo-upstream-1", {
    location: "us-central1",
    repositoryId: "my-repository-upstream-1",
    description: "example docker repository (upstream source) 1",
    format: "DOCKER",
});
const my_repo_upstream_2 = new gcp.artifactregistry.Repository("my-repo-upstream-2", {
    location: "us-central1",
    repositoryId: "my-repository-upstream-2",
    description: "example docker repository (upstream source) 2",
    format: "DOCKER",
});
const my_repo = new gcp.artifactregistry.Repository("my-repo", {
    location: "us-central1",
    repositoryId: "my-repository",
    description: "example virtual docker repository",
    format: "DOCKER",
    mode: "VIRTUAL_REPOSITORY",
    virtualRepositoryConfig: {
        upstreamPolicies: [
            {
                id: "my-repository-upstream-1",
                repository: my_repo_upstream_1.id,
                priority: 20,
            },
            {
                id: "my-repository-upstream-2",
                repository: my_repo_upstream_2.id,
                priority: 10,
            },
        ],
    },
});
import pulumi
import pulumi_gcp as gcp

my_repo_upstream_1 = gcp.artifactregistry.Repository("my-repo-upstream-1",
    location="us-central1",
    repository_id="my-repository-upstream-1",
    description="example docker repository (upstream source) 1",
    format="DOCKER")
my_repo_upstream_2 = gcp.artifactregistry.Repository("my-repo-upstream-2",
    location="us-central1",
    repository_id="my-repository-upstream-2",
    description="example docker repository (upstream source) 2",
    format="DOCKER")
my_repo = gcp.artifactregistry.Repository("my-repo",
    location="us-central1",
    repository_id="my-repository",
    description="example virtual docker repository",
    format="DOCKER",
    mode="VIRTUAL_REPOSITORY",
    virtual_repository_config={
        "upstream_policies": [
            {
                "id": "my-repository-upstream-1",
                "repository": my_repo_upstream_1.id,
                "priority": 20,
            },
            {
                "id": "my-repository-upstream-2",
                "repository": my_repo_upstream_2.id,
                "priority": 10,
            },
        ],
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/artifactregistry"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		my_repo_upstream_1, err := artifactregistry.NewRepository(ctx, "my-repo-upstream-1", &artifactregistry.RepositoryArgs{
			Location:     pulumi.String("us-central1"),
			RepositoryId: pulumi.String("my-repository-upstream-1"),
			Description:  pulumi.String("example docker repository (upstream source) 1"),
			Format:       pulumi.String("DOCKER"),
		})
		if err != nil {
			return err
		}
		my_repo_upstream_2, err := artifactregistry.NewRepository(ctx, "my-repo-upstream-2", &artifactregistry.RepositoryArgs{
			Location:     pulumi.String("us-central1"),
			RepositoryId: pulumi.String("my-repository-upstream-2"),
			Description:  pulumi.String("example docker repository (upstream source) 2"),
			Format:       pulumi.String("DOCKER"),
		})
		if err != nil {
			return err
		}
		_, err = artifactregistry.NewRepository(ctx, "my-repo", &artifactregistry.RepositoryArgs{
			Location:     pulumi.String("us-central1"),
			RepositoryId: pulumi.String("my-repository"),
			Description:  pulumi.String("example virtual docker repository"),
			Format:       pulumi.String("DOCKER"),
			Mode:         pulumi.String("VIRTUAL_REPOSITORY"),
			VirtualRepositoryConfig: &artifactregistry.RepositoryVirtualRepositoryConfigArgs{
				UpstreamPolicies: artifactregistry.RepositoryVirtualRepositoryConfigUpstreamPolicyArray{
					&artifactregistry.RepositoryVirtualRepositoryConfigUpstreamPolicyArgs{
						Id:         pulumi.String("my-repository-upstream-1"),
						Repository: my_repo_upstream_1.ID(),
						Priority:   pulumi.Int(20),
					},
					&artifactregistry.RepositoryVirtualRepositoryConfigUpstreamPolicyArgs{
						Id:         pulumi.String("my-repository-upstream-2"),
						Repository: my_repo_upstream_2.ID(),
						Priority:   pulumi.Int(10),
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var my_repo_upstream_1 = new Gcp.ArtifactRegistry.Repository("my-repo-upstream-1", new()
    {
        Location = "us-central1",
        RepositoryId = "my-repository-upstream-1",
        Description = "example docker repository (upstream source) 1",
        Format = "DOCKER",
    });

    var my_repo_upstream_2 = new Gcp.ArtifactRegistry.Repository("my-repo-upstream-2", new()
    {
        Location = "us-central1",
        RepositoryId = "my-repository-upstream-2",
        Description = "example docker repository (upstream source) 2",
        Format = "DOCKER",
    });

    var my_repo = new Gcp.ArtifactRegistry.Repository("my-repo", new()
    {
        Location = "us-central1",
        RepositoryId = "my-repository",
        Description = "example virtual docker repository",
        Format = "DOCKER",
        Mode = "VIRTUAL_REPOSITORY",
        VirtualRepositoryConfig = new Gcp.ArtifactRegistry.Inputs.RepositoryVirtualRepositoryConfigArgs
        {
            UpstreamPolicies = new[]
            {
                new Gcp.ArtifactRegistry.Inputs.RepositoryVirtualRepositoryConfigUpstreamPolicyArgs
                {
                    Id = "my-repository-upstream-1",
                    Repository = my_repo_upstream_1.Id,
                    Priority = 20,
                },
                new Gcp.ArtifactRegistry.Inputs.RepositoryVirtualRepositoryConfigUpstreamPolicyArgs
                {
                    Id = "my-repository-upstream-2",
                    Repository = my_repo_upstream_2.Id,
                    Priority = 10,
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.artifactregistry.Repository;
import com.pulumi.gcp.artifactregistry.RepositoryArgs;
import com.pulumi.gcp.artifactregistry.inputs.RepositoryVirtualRepositoryConfigArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var my_repo_upstream_1 = new Repository("my-repo-upstream-1", RepositoryArgs.builder()
            .location("us-central1")
            .repositoryId("my-repository-upstream-1")
            .description("example docker repository (upstream source) 1")
            .format("DOCKER")
            .build());

        var my_repo_upstream_2 = new Repository("my-repo-upstream-2", RepositoryArgs.builder()
            .location("us-central1")
            .repositoryId("my-repository-upstream-2")
            .description("example docker repository (upstream source) 2")
            .format("DOCKER")
            .build());

        var my_repo = new Repository("my-repo", RepositoryArgs.builder()
            .location("us-central1")
            .repositoryId("my-repository")
            .description("example virtual docker repository")
            .format("DOCKER")
            .mode("VIRTUAL_REPOSITORY")
            .virtualRepositoryConfig(RepositoryVirtualRepositoryConfigArgs.builder()
                .upstreamPolicies(                
                    RepositoryVirtualRepositoryConfigUpstreamPolicyArgs.builder()
                        .id("my-repository-upstream-1")
                        .repository(my_repo_upstream_1.id())
                        .priority(20)
                        .build(),
                    RepositoryVirtualRepositoryConfigUpstreamPolicyArgs.builder()
                        .id("my-repository-upstream-2")
                        .repository(my_repo_upstream_2.id())
                        .priority(10)
                        .build())
                .build())
            .build());

    }
}
resources:
  my-repo-upstream-1:
    type: gcp:artifactregistry:Repository
    properties:
      location: us-central1
      repositoryId: my-repository-upstream-1
      description: example docker repository (upstream source) 1
      format: DOCKER
  my-repo-upstream-2:
    type: gcp:artifactregistry:Repository
    properties:
      location: us-central1
      repositoryId: my-repository-upstream-2
      description: example docker repository (upstream source) 2
      format: DOCKER
  my-repo:
    type: gcp:artifactregistry:Repository
    properties:
      location: us-central1
      repositoryId: my-repository
      description: example virtual docker repository
      format: DOCKER
      mode: VIRTUAL_REPOSITORY
      virtualRepositoryConfig:
        upstreamPolicies:
          - id: my-repository-upstream-1
            repository: ${["my-repo-upstream-1"].id}
            priority: 20
          - id: my-repository-upstream-2
            repository: ${["my-repo-upstream-2"].id}
            priority: 10

Setting mode to “VIRTUAL_REPOSITORY” creates a routing layer. The virtualRepositoryConfig defines upstreamPolicies that list source repositories and their priorities. Lower priority numbers are checked first; if an artifact isn’t found, the next priority is tried.

Cache Docker Hub images as a pull-through proxy

Remote repositories cache artifacts from external registries, reducing external bandwidth and improving pull performance.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const my_repo = new gcp.artifactregistry.Repository("my-repo", {
    location: "us-central1",
    repositoryId: "my-repository",
    description: "example remote docker repository",
    format: "DOCKER",
    mode: "REMOTE_REPOSITORY",
    remoteRepositoryConfig: {
        description: "docker hub",
        dockerRepository: {
            publicRepository: "DOCKER_HUB",
        },
    },
});
import pulumi
import pulumi_gcp as gcp

my_repo = gcp.artifactregistry.Repository("my-repo",
    location="us-central1",
    repository_id="my-repository",
    description="example remote docker repository",
    format="DOCKER",
    mode="REMOTE_REPOSITORY",
    remote_repository_config={
        "description": "docker hub",
        "docker_repository": {
            "public_repository": "DOCKER_HUB",
        },
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/artifactregistry"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := artifactregistry.NewRepository(ctx, "my-repo", &artifactregistry.RepositoryArgs{
			Location:     pulumi.String("us-central1"),
			RepositoryId: pulumi.String("my-repository"),
			Description:  pulumi.String("example remote docker repository"),
			Format:       pulumi.String("DOCKER"),
			Mode:         pulumi.String("REMOTE_REPOSITORY"),
			RemoteRepositoryConfig: &artifactregistry.RepositoryRemoteRepositoryConfigArgs{
				Description: pulumi.String("docker hub"),
				DockerRepository: &artifactregistry.RepositoryRemoteRepositoryConfigDockerRepositoryArgs{
					PublicRepository: pulumi.String("DOCKER_HUB"),
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var my_repo = new Gcp.ArtifactRegistry.Repository("my-repo", new()
    {
        Location = "us-central1",
        RepositoryId = "my-repository",
        Description = "example remote docker repository",
        Format = "DOCKER",
        Mode = "REMOTE_REPOSITORY",
        RemoteRepositoryConfig = new Gcp.ArtifactRegistry.Inputs.RepositoryRemoteRepositoryConfigArgs
        {
            Description = "docker hub",
            DockerRepository = new Gcp.ArtifactRegistry.Inputs.RepositoryRemoteRepositoryConfigDockerRepositoryArgs
            {
                PublicRepository = "DOCKER_HUB",
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.artifactregistry.Repository;
import com.pulumi.gcp.artifactregistry.RepositoryArgs;
import com.pulumi.gcp.artifactregistry.inputs.RepositoryRemoteRepositoryConfigArgs;
import com.pulumi.gcp.artifactregistry.inputs.RepositoryRemoteRepositoryConfigDockerRepositoryArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var my_repo = new Repository("my-repo", RepositoryArgs.builder()
            .location("us-central1")
            .repositoryId("my-repository")
            .description("example remote docker repository")
            .format("DOCKER")
            .mode("REMOTE_REPOSITORY")
            .remoteRepositoryConfig(RepositoryRemoteRepositoryConfigArgs.builder()
                .description("docker hub")
                .dockerRepository(RepositoryRemoteRepositoryConfigDockerRepositoryArgs.builder()
                    .publicRepository("DOCKER_HUB")
                    .build())
                .build())
            .build());

    }
}
resources:
  my-repo:
    type: gcp:artifactregistry:Repository
    properties:
      location: us-central1
      repositoryId: my-repository
      description: example remote docker repository
      format: DOCKER
      mode: REMOTE_REPOSITORY
      remoteRepositoryConfig:
        description: docker hub
        dockerRepository:
          publicRepository: DOCKER_HUB

Setting mode to “REMOTE_REPOSITORY” creates a caching proxy. The remoteRepositoryConfig specifies the upstream source. Setting publicRepository to “DOCKER_HUB” configures Docker Hub as the upstream; pulls are cached locally after the first request.

Automate artifact deletion with cleanup policies

Repositories accumulate old versions and untagged images over time. Cleanup policies automatically delete artifacts based on rules.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const my_repo = new gcp.artifactregistry.Repository("my-repo", {
    location: "us-central1",
    repositoryId: "my-repository",
    description: "example docker repository with cleanup policies",
    format: "DOCKER",
    cleanupPolicyDryRun: false,
    cleanupPolicies: [
        {
            id: "delete-untagged",
            action: "DELETE",
            condition: {
                tagState: "UNTAGGED",
            },
        },
        {
            id: "keep-new-untagged",
            action: "KEEP",
            condition: {
                tagState: "UNTAGGED",
                newerThan: "7d",
            },
        },
        {
            id: "delete-prerelease",
            action: "DELETE",
            condition: {
                tagState: "TAGGED",
                tagPrefixes: [
                    "alpha",
                    "v0",
                ],
                olderThan: "30d",
            },
        },
        {
            id: "keep-tagged-release",
            action: "KEEP",
            condition: {
                tagState: "TAGGED",
                tagPrefixes: ["release"],
                packageNamePrefixes: [
                    "webapp",
                    "mobile",
                ],
            },
        },
        {
            id: "keep-minimum-versions",
            action: "KEEP",
            mostRecentVersions: {
                packageNamePrefixes: [
                    "webapp",
                    "mobile",
                    "sandbox",
                ],
                keepCount: 5,
            },
        },
    ],
});
import pulumi
import pulumi_gcp as gcp

my_repo = gcp.artifactregistry.Repository("my-repo",
    location="us-central1",
    repository_id="my-repository",
    description="example docker repository with cleanup policies",
    format="DOCKER",
    cleanup_policy_dry_run=False,
    cleanup_policies=[
        {
            "id": "delete-untagged",
            "action": "DELETE",
            "condition": {
                "tag_state": "UNTAGGED",
            },
        },
        {
            "id": "keep-new-untagged",
            "action": "KEEP",
            "condition": {
                "tag_state": "UNTAGGED",
                "newer_than": "7d",
            },
        },
        {
            "id": "delete-prerelease",
            "action": "DELETE",
            "condition": {
                "tag_state": "TAGGED",
                "tag_prefixes": [
                    "alpha",
                    "v0",
                ],
                "older_than": "30d",
            },
        },
        {
            "id": "keep-tagged-release",
            "action": "KEEP",
            "condition": {
                "tag_state": "TAGGED",
                "tag_prefixes": ["release"],
                "package_name_prefixes": [
                    "webapp",
                    "mobile",
                ],
            },
        },
        {
            "id": "keep-minimum-versions",
            "action": "KEEP",
            "most_recent_versions": {
                "package_name_prefixes": [
                    "webapp",
                    "mobile",
                    "sandbox",
                ],
                "keep_count": 5,
            },
        },
    ])
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/artifactregistry"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := artifactregistry.NewRepository(ctx, "my-repo", &artifactregistry.RepositoryArgs{
			Location:            pulumi.String("us-central1"),
			RepositoryId:        pulumi.String("my-repository"),
			Description:         pulumi.String("example docker repository with cleanup policies"),
			Format:              pulumi.String("DOCKER"),
			CleanupPolicyDryRun: pulumi.Bool(false),
			CleanupPolicies: artifactregistry.RepositoryCleanupPolicyArray{
				&artifactregistry.RepositoryCleanupPolicyArgs{
					Id:     pulumi.String("delete-untagged"),
					Action: pulumi.String("DELETE"),
					Condition: &artifactregistry.RepositoryCleanupPolicyConditionArgs{
						TagState: pulumi.String("UNTAGGED"),
					},
				},
				&artifactregistry.RepositoryCleanupPolicyArgs{
					Id:     pulumi.String("keep-new-untagged"),
					Action: pulumi.String("KEEP"),
					Condition: &artifactregistry.RepositoryCleanupPolicyConditionArgs{
						TagState:  pulumi.String("UNTAGGED"),
						NewerThan: pulumi.String("7d"),
					},
				},
				&artifactregistry.RepositoryCleanupPolicyArgs{
					Id:     pulumi.String("delete-prerelease"),
					Action: pulumi.String("DELETE"),
					Condition: &artifactregistry.RepositoryCleanupPolicyConditionArgs{
						TagState: pulumi.String("TAGGED"),
						TagPrefixes: pulumi.StringArray{
							pulumi.String("alpha"),
							pulumi.String("v0"),
						},
						OlderThan: pulumi.String("30d"),
					},
				},
				&artifactregistry.RepositoryCleanupPolicyArgs{
					Id:     pulumi.String("keep-tagged-release"),
					Action: pulumi.String("KEEP"),
					Condition: &artifactregistry.RepositoryCleanupPolicyConditionArgs{
						TagState: pulumi.String("TAGGED"),
						TagPrefixes: pulumi.StringArray{
							pulumi.String("release"),
						},
						PackageNamePrefixes: pulumi.StringArray{
							pulumi.String("webapp"),
							pulumi.String("mobile"),
						},
					},
				},
				&artifactregistry.RepositoryCleanupPolicyArgs{
					Id:     pulumi.String("keep-minimum-versions"),
					Action: pulumi.String("KEEP"),
					MostRecentVersions: &artifactregistry.RepositoryCleanupPolicyMostRecentVersionsArgs{
						PackageNamePrefixes: pulumi.StringArray{
							pulumi.String("webapp"),
							pulumi.String("mobile"),
							pulumi.String("sandbox"),
						},
						KeepCount: pulumi.Int(5),
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var my_repo = new Gcp.ArtifactRegistry.Repository("my-repo", new()
    {
        Location = "us-central1",
        RepositoryId = "my-repository",
        Description = "example docker repository with cleanup policies",
        Format = "DOCKER",
        CleanupPolicyDryRun = false,
        CleanupPolicies = new[]
        {
            new Gcp.ArtifactRegistry.Inputs.RepositoryCleanupPolicyArgs
            {
                Id = "delete-untagged",
                Action = "DELETE",
                Condition = new Gcp.ArtifactRegistry.Inputs.RepositoryCleanupPolicyConditionArgs
                {
                    TagState = "UNTAGGED",
                },
            },
            new Gcp.ArtifactRegistry.Inputs.RepositoryCleanupPolicyArgs
            {
                Id = "keep-new-untagged",
                Action = "KEEP",
                Condition = new Gcp.ArtifactRegistry.Inputs.RepositoryCleanupPolicyConditionArgs
                {
                    TagState = "UNTAGGED",
                    NewerThan = "7d",
                },
            },
            new Gcp.ArtifactRegistry.Inputs.RepositoryCleanupPolicyArgs
            {
                Id = "delete-prerelease",
                Action = "DELETE",
                Condition = new Gcp.ArtifactRegistry.Inputs.RepositoryCleanupPolicyConditionArgs
                {
                    TagState = "TAGGED",
                    TagPrefixes = new[]
                    {
                        "alpha",
                        "v0",
                    },
                    OlderThan = "30d",
                },
            },
            new Gcp.ArtifactRegistry.Inputs.RepositoryCleanupPolicyArgs
            {
                Id = "keep-tagged-release",
                Action = "KEEP",
                Condition = new Gcp.ArtifactRegistry.Inputs.RepositoryCleanupPolicyConditionArgs
                {
                    TagState = "TAGGED",
                    TagPrefixes = new[]
                    {
                        "release",
                    },
                    PackageNamePrefixes = new[]
                    {
                        "webapp",
                        "mobile",
                    },
                },
            },
            new Gcp.ArtifactRegistry.Inputs.RepositoryCleanupPolicyArgs
            {
                Id = "keep-minimum-versions",
                Action = "KEEP",
                MostRecentVersions = new Gcp.ArtifactRegistry.Inputs.RepositoryCleanupPolicyMostRecentVersionsArgs
                {
                    PackageNamePrefixes = new[]
                    {
                        "webapp",
                        "mobile",
                        "sandbox",
                    },
                    KeepCount = 5,
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.artifactregistry.Repository;
import com.pulumi.gcp.artifactregistry.RepositoryArgs;
import com.pulumi.gcp.artifactregistry.inputs.RepositoryCleanupPolicyArgs;
import com.pulumi.gcp.artifactregistry.inputs.RepositoryCleanupPolicyConditionArgs;
import com.pulumi.gcp.artifactregistry.inputs.RepositoryCleanupPolicyMostRecentVersionsArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var my_repo = new Repository("my-repo", RepositoryArgs.builder()
            .location("us-central1")
            .repositoryId("my-repository")
            .description("example docker repository with cleanup policies")
            .format("DOCKER")
            .cleanupPolicyDryRun(false)
            .cleanupPolicies(            
                RepositoryCleanupPolicyArgs.builder()
                    .id("delete-untagged")
                    .action("DELETE")
                    .condition(RepositoryCleanupPolicyConditionArgs.builder()
                        .tagState("UNTAGGED")
                        .build())
                    .build(),
                RepositoryCleanupPolicyArgs.builder()
                    .id("keep-new-untagged")
                    .action("KEEP")
                    .condition(RepositoryCleanupPolicyConditionArgs.builder()
                        .tagState("UNTAGGED")
                        .newerThan("7d")
                        .build())
                    .build(),
                RepositoryCleanupPolicyArgs.builder()
                    .id("delete-prerelease")
                    .action("DELETE")
                    .condition(RepositoryCleanupPolicyConditionArgs.builder()
                        .tagState("TAGGED")
                        .tagPrefixes(                        
                            "alpha",
                            "v0")
                        .olderThan("30d")
                        .build())
                    .build(),
                RepositoryCleanupPolicyArgs.builder()
                    .id("keep-tagged-release")
                    .action("KEEP")
                    .condition(RepositoryCleanupPolicyConditionArgs.builder()
                        .tagState("TAGGED")
                        .tagPrefixes("release")
                        .packageNamePrefixes(                        
                            "webapp",
                            "mobile")
                        .build())
                    .build(),
                RepositoryCleanupPolicyArgs.builder()
                    .id("keep-minimum-versions")
                    .action("KEEP")
                    .mostRecentVersions(RepositoryCleanupPolicyMostRecentVersionsArgs.builder()
                        .packageNamePrefixes(                        
                            "webapp",
                            "mobile",
                            "sandbox")
                        .keepCount(5)
                        .build())
                    .build())
            .build());

    }
}
resources:
  my-repo:
    type: gcp:artifactregistry:Repository
    properties:
      location: us-central1
      repositoryId: my-repository
      description: example docker repository with cleanup policies
      format: DOCKER
      cleanupPolicyDryRun: false
      cleanupPolicies:
        - id: delete-untagged
          action: DELETE
          condition:
            tagState: UNTAGGED
        - id: keep-new-untagged
          action: KEEP
          condition:
            tagState: UNTAGGED
            newerThan: 7d
        - id: delete-prerelease
          action: DELETE
          condition:
            tagState: TAGGED
            tagPrefixes:
              - alpha
              - v0
            olderThan: 30d
        - id: keep-tagged-release
          action: KEEP
          condition:
            tagState: TAGGED
            tagPrefixes:
              - release
            packageNamePrefixes:
              - webapp
              - mobile
        - id: keep-minimum-versions
          action: KEEP
          mostRecentVersions:
            packageNamePrefixes:
              - webapp
              - mobile
              - sandbox
            keepCount: 5

The cleanupPolicies array defines deletion rules. Each policy has an action (DELETE or KEEP) and a condition (tagState, age, tag prefixes, or version count). Policies are evaluated in order; KEEP policies override DELETE policies. Setting cleanupPolicyDryRun to false enables actual deletion; true only logs what would be deleted.

Authenticate to Docker Hub with stored credentials

Private Docker Hub repositories or rate-limited public access require authentication stored in Secret Manager.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const project = gcp.organizations.getProject({});
const example_remote_secret = new gcp.secretmanager.Secret("example-remote-secret", {
    secretId: "example-secret",
    replication: {
        auto: {},
    },
});
const example_remote_secretVersion = new gcp.secretmanager.SecretVersion("example-remote-secret_version", {
    secret: example_remote_secret.id,
    secretData: "remote-password",
});
const secret_access = new gcp.secretmanager.SecretIamMember("secret-access", {
    secretId: example_remote_secret.id,
    role: "roles/secretmanager.secretAccessor",
    member: project.then(project => `serviceAccount:service-${project.number}@gcp-sa-artifactregistry.iam.gserviceaccount.com`),
});
const my_repo = new gcp.artifactregistry.Repository("my-repo", {
    location: "us-central1",
    repositoryId: "example-dockerhub-remote",
    description: "example remote dockerhub repository with credentials",
    format: "DOCKER",
    mode: "REMOTE_REPOSITORY",
    remoteRepositoryConfig: {
        description: "docker hub with custom credentials",
        disableUpstreamValidation: true,
        dockerRepository: {
            publicRepository: "DOCKER_HUB",
        },
        upstreamCredentials: {
            usernamePasswordCredentials: {
                username: "remote-username",
                passwordSecretVersion: example_remote_secretVersion.name,
            },
        },
    },
});
import pulumi
import pulumi_gcp as gcp

project = gcp.organizations.get_project()
example_remote_secret = gcp.secretmanager.Secret("example-remote-secret",
    secret_id="example-secret",
    replication={
        "auto": {},
    })
example_remote_secret_version = gcp.secretmanager.SecretVersion("example-remote-secret_version",
    secret=example_remote_secret.id,
    secret_data="remote-password")
secret_access = gcp.secretmanager.SecretIamMember("secret-access",
    secret_id=example_remote_secret.id,
    role="roles/secretmanager.secretAccessor",
    member=f"serviceAccount:service-{project.number}@gcp-sa-artifactregistry.iam.gserviceaccount.com")
my_repo = gcp.artifactregistry.Repository("my-repo",
    location="us-central1",
    repository_id="example-dockerhub-remote",
    description="example remote dockerhub repository with credentials",
    format="DOCKER",
    mode="REMOTE_REPOSITORY",
    remote_repository_config={
        "description": "docker hub with custom credentials",
        "disable_upstream_validation": True,
        "docker_repository": {
            "public_repository": "DOCKER_HUB",
        },
        "upstream_credentials": {
            "username_password_credentials": {
                "username": "remote-username",
                "password_secret_version": example_remote_secret_version.name,
            },
        },
    })
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/artifactregistry"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/secretmanager"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		project, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
		if err != nil {
			return err
		}
		example_remote_secret, err := secretmanager.NewSecret(ctx, "example-remote-secret", &secretmanager.SecretArgs{
			SecretId: pulumi.String("example-secret"),
			Replication: &secretmanager.SecretReplicationArgs{
				Auto: &secretmanager.SecretReplicationAutoArgs{},
			},
		})
		if err != nil {
			return err
		}
		example_remote_secretVersion, err := secretmanager.NewSecretVersion(ctx, "example-remote-secret_version", &secretmanager.SecretVersionArgs{
			Secret:     example_remote_secret.ID(),
			SecretData: pulumi.String("remote-password"),
		})
		if err != nil {
			return err
		}
		_, err = secretmanager.NewSecretIamMember(ctx, "secret-access", &secretmanager.SecretIamMemberArgs{
			SecretId: example_remote_secret.ID(),
			Role:     pulumi.String("roles/secretmanager.secretAccessor"),
			Member:   pulumi.Sprintf("serviceAccount:service-%v@gcp-sa-artifactregistry.iam.gserviceaccount.com", project.Number),
		})
		if err != nil {
			return err
		}
		_, err = artifactregistry.NewRepository(ctx, "my-repo", &artifactregistry.RepositoryArgs{
			Location:     pulumi.String("us-central1"),
			RepositoryId: pulumi.String("example-dockerhub-remote"),
			Description:  pulumi.String("example remote dockerhub repository with credentials"),
			Format:       pulumi.String("DOCKER"),
			Mode:         pulumi.String("REMOTE_REPOSITORY"),
			RemoteRepositoryConfig: &artifactregistry.RepositoryRemoteRepositoryConfigArgs{
				Description:               pulumi.String("docker hub with custom credentials"),
				DisableUpstreamValidation: pulumi.Bool(true),
				DockerRepository: &artifactregistry.RepositoryRemoteRepositoryConfigDockerRepositoryArgs{
					PublicRepository: pulumi.String("DOCKER_HUB"),
				},
				UpstreamCredentials: &artifactregistry.RepositoryRemoteRepositoryConfigUpstreamCredentialsArgs{
					UsernamePasswordCredentials: &artifactregistry.RepositoryRemoteRepositoryConfigUpstreamCredentialsUsernamePasswordCredentialsArgs{
						Username:              pulumi.String("remote-username"),
						PasswordSecretVersion: example_remote_secretVersion.Name,
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var project = Gcp.Organizations.GetProject.Invoke();

    var example_remote_secret = new Gcp.SecretManager.Secret("example-remote-secret", new()
    {
        SecretId = "example-secret",
        Replication = new Gcp.SecretManager.Inputs.SecretReplicationArgs
        {
            Auto = null,
        },
    });

    var example_remote_secretVersion = new Gcp.SecretManager.SecretVersion("example-remote-secret_version", new()
    {
        Secret = example_remote_secret.Id,
        SecretData = "remote-password",
    });

    var secret_access = new Gcp.SecretManager.SecretIamMember("secret-access", new()
    {
        SecretId = example_remote_secret.Id,
        Role = "roles/secretmanager.secretAccessor",
        Member = $"serviceAccount:service-{project.Apply(getProjectResult => getProjectResult.Number)}@gcp-sa-artifactregistry.iam.gserviceaccount.com",
    });

    var my_repo = new Gcp.ArtifactRegistry.Repository("my-repo", new()
    {
        Location = "us-central1",
        RepositoryId = "example-dockerhub-remote",
        Description = "example remote dockerhub repository with credentials",
        Format = "DOCKER",
        Mode = "REMOTE_REPOSITORY",
        RemoteRepositoryConfig = new Gcp.ArtifactRegistry.Inputs.RepositoryRemoteRepositoryConfigArgs
        {
            Description = "docker hub with custom credentials",
            DisableUpstreamValidation = true,
            DockerRepository = new Gcp.ArtifactRegistry.Inputs.RepositoryRemoteRepositoryConfigDockerRepositoryArgs
            {
                PublicRepository = "DOCKER_HUB",
            },
            UpstreamCredentials = new Gcp.ArtifactRegistry.Inputs.RepositoryRemoteRepositoryConfigUpstreamCredentialsArgs
            {
                UsernamePasswordCredentials = new Gcp.ArtifactRegistry.Inputs.RepositoryRemoteRepositoryConfigUpstreamCredentialsUsernamePasswordCredentialsArgs
                {
                    Username = "remote-username",
                    PasswordSecretVersion = example_remote_secretVersion.Name,
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import com.pulumi.gcp.secretmanager.Secret;
import com.pulumi.gcp.secretmanager.SecretArgs;
import com.pulumi.gcp.secretmanager.inputs.SecretReplicationArgs;
import com.pulumi.gcp.secretmanager.inputs.SecretReplicationAutoArgs;
import com.pulumi.gcp.secretmanager.SecretVersion;
import com.pulumi.gcp.secretmanager.SecretVersionArgs;
import com.pulumi.gcp.secretmanager.SecretIamMember;
import com.pulumi.gcp.secretmanager.SecretIamMemberArgs;
import com.pulumi.gcp.artifactregistry.Repository;
import com.pulumi.gcp.artifactregistry.RepositoryArgs;
import com.pulumi.gcp.artifactregistry.inputs.RepositoryRemoteRepositoryConfigArgs;
import com.pulumi.gcp.artifactregistry.inputs.RepositoryRemoteRepositoryConfigDockerRepositoryArgs;
import com.pulumi.gcp.artifactregistry.inputs.RepositoryRemoteRepositoryConfigUpstreamCredentialsArgs;
import com.pulumi.gcp.artifactregistry.inputs.RepositoryRemoteRepositoryConfigUpstreamCredentialsUsernamePasswordCredentialsArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        final var project = OrganizationsFunctions.getProject(GetProjectArgs.builder()
            .build());

        var example_remote_secret = new Secret("example-remote-secret", SecretArgs.builder()
            .secretId("example-secret")
            .replication(SecretReplicationArgs.builder()
                .auto(SecretReplicationAutoArgs.builder()
                    .build())
                .build())
            .build());

        var example_remote_secretVersion = new SecretVersion("example-remote-secretVersion", SecretVersionArgs.builder()
            .secret(example_remote_secret.id())
            .secretData("remote-password")
            .build());

        var secret_access = new SecretIamMember("secret-access", SecretIamMemberArgs.builder()
            .secretId(example_remote_secret.id())
            .role("roles/secretmanager.secretAccessor")
            .member(String.format("serviceAccount:service-%s@gcp-sa-artifactregistry.iam.gserviceaccount.com", project.number()))
            .build());

        var my_repo = new Repository("my-repo", RepositoryArgs.builder()
            .location("us-central1")
            .repositoryId("example-dockerhub-remote")
            .description("example remote dockerhub repository with credentials")
            .format("DOCKER")
            .mode("REMOTE_REPOSITORY")
            .remoteRepositoryConfig(RepositoryRemoteRepositoryConfigArgs.builder()
                .description("docker hub with custom credentials")
                .disableUpstreamValidation(true)
                .dockerRepository(RepositoryRemoteRepositoryConfigDockerRepositoryArgs.builder()
                    .publicRepository("DOCKER_HUB")
                    .build())
                .upstreamCredentials(RepositoryRemoteRepositoryConfigUpstreamCredentialsArgs.builder()
                    .usernamePasswordCredentials(RepositoryRemoteRepositoryConfigUpstreamCredentialsUsernamePasswordCredentialsArgs.builder()
                        .username("remote-username")
                        .passwordSecretVersion(example_remote_secretVersion.name())
                        .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  example-remote-secret:
    type: gcp:secretmanager:Secret
    properties:
      secretId: example-secret
      replication:
        auto: {}
  example-remote-secretVersion:
    type: gcp:secretmanager:SecretVersion
    name: example-remote-secret_version
    properties:
      secret: ${["example-remote-secret"].id}
      secretData: remote-password
  secret-access:
    type: gcp:secretmanager:SecretIamMember
    properties:
      secretId: ${["example-remote-secret"].id}
      role: roles/secretmanager.secretAccessor
      member: serviceAccount:service-${project.number}@gcp-sa-artifactregistry.iam.gserviceaccount.com
  my-repo:
    type: gcp:artifactregistry:Repository
    properties:
      location: us-central1
      repositoryId: example-dockerhub-remote
      description: example remote dockerhub repository with credentials
      format: DOCKER
      mode: REMOTE_REPOSITORY
      remoteRepositoryConfig:
        description: docker hub with custom credentials
        disableUpstreamValidation: true
        dockerRepository:
          publicRepository: DOCKER_HUB
        upstreamCredentials:
          usernamePasswordCredentials:
            username: remote-username
            passwordSecretVersion: ${["example-remote-secretVersion"].name}
variables:
  project:
    fn::invoke:
      function: gcp:organizations:getProject
      arguments: {}

The upstreamCredentials block references a Secret Manager secret containing the password. The usernamePasswordCredentials specify the username and passwordSecretVersion. The SecretIamMember grants the Artifact Registry service account permission to read the secret. Setting disableUpstreamValidation to true skips connectivity checks during repository creation.

Beyond these examples

These snippets focus on specific repository-level features: regional and multi-region deployment, virtual and remote repository modes, cleanup policies and encryption, and authentication for external registries. They’re intentionally minimal rather than full artifact management solutions.

The examples may reference pre-existing infrastructure such as KMS keys for customer-managed encryption and upstream repositories for virtual repository routing. They focus on configuring the repository rather than provisioning everything around it.

To keep things focused, common repository patterns are omitted, including:

  • Format-specific configurations (Maven, NPM, Python, APT, YUM)
  • Vulnerability scanning configuration
  • Custom remote repository URIs for non-Docker formats
  • Common repository mode for Artifact Registry pull-through caching

These omissions are intentional: the goal is to illustrate how each repository feature is wired, not provide drop-in artifact management modules. See the Artifact Registry Repository resource reference for all available configuration options.

Let's create GCP Artifact Registry Repositories

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Repository Configuration & Immutability
What properties can't I change after creating a repository?
The following properties are immutable and require resource recreation if changed: format, location, project, repositoryId, kmsKeyName, mode, and remoteRepositoryConfig.
What repository formats are supported?
Artifact Registry supports DOCKER, MAVEN, NPM, PYTHON, APT, and YUM formats. Alpha formats require membership in the alpha user group.
Can I create a multi-region repository?
Yes, set location to asia, europe, or us instead of a specific region for multi-region repositories.
Repository Modes & Types
What's the difference between repository modes?
Repositories have three modes: STANDARD_REPOSITORY (default, stores your artifacts), VIRTUAL_REPOSITORY (aggregates multiple upstream repositories), and REMOTE_REPOSITORY (caches artifacts from external sources like Docker Hub).
How do I create a virtual repository with multiple upstream sources?
Set mode to VIRTUAL_REPOSITORY and configure virtualRepositoryConfig.upstreamPolicies with repository references and priority values. Lower priority numbers are checked first.
What remote repository sources can I use?
You can use public repositories (like DOCKER_HUB, DEBIAN, ROCKY) or custom repositories by specifying a URI. For another Artifact Registry repository, use commonRepository.uri with the repository ID or full URL.
Authentication & Security
How do I authenticate to a remote repository?
Configure remoteRepositoryConfig.upstreamCredentials.usernamePasswordCredentials with a username and passwordSecretVersion pointing to a Secret Manager secret. Grant the Artifact Registry service account (service-PROJECT_NUMBER@gcp-sa-artifactregistry.iam.gserviceaccount.com) the roles/secretmanager.secretAccessor role.
How do I enable customer-managed encryption?
Set kmsKeyName to your Cloud KMS key resource name. You must grant the Artifact Registry service account (service-PROJECT_NUMBER@gcp-sa-artifactregistry.iam.gserviceaccount.com) the roles/cloudkms.cryptoKeyEncrypterDecrypter role before creating the repository. Use dependsOn to ensure the IAM binding is created first.
Cleanup Policies & Maintenance
How do I configure automatic cleanup of old artifacts?
Use cleanupPolicies with conditions like tagState (TAGGED/UNTAGGED), newerThan/olderThan (duration), tagPrefixes, and packageNamePrefixes. Each policy has an action (DELETE or KEEP) and a unique ID. Use mostRecentVersions.keepCount to retain a minimum number of versions.
How do I test cleanup policies without deleting artifacts?
Set cleanupPolicyDryRun to true to prevent the cleanup pipeline from actually deleting versions while you test your policies.
Labels & Metadata
Why aren't all my repository labels showing up in Pulumi?
The labels field is non-authoritative and only manages labels present in your configuration. Labels set outside Pulumi won’t be removed. Use the effectiveLabels output property to see all labels on the resource.
Docker-Specific Configuration
How do I prevent Docker tags from being overwritten?
Set dockerConfig.immutableTags to true to prevent tag mutation in Docker repositories.

Using a different cloud?

Explore containers guides for other cloud providers: