Exposing the Registry

By default, use the internal service for workloads inside the cluster. Expose Alauda Container Platform Registry only when developer machines, CI systems, or other external clients must push or pull images.

Prerequisites

  • Registry is installed and available.
  • You have permission to update Config/cluster and create or reference TLS Secrets in image-registry-system.
  • The cluster has an Ingress controller that can route traffic to image-registry-system.
  • For a custom host, prepare a DNS record and a TLS certificate whose Subject Alternative Name matches the Registry hostname.

Expose the Default Registry

Enable the default external endpoint in Config/cluster:

kubectl patch configs.imageregistry.operator.alauda.io cluster \
  --type=merge \
  -p '{"spec":{"defaultRoute":true}}'

The Operator renders a Kubernetes Ingress named default-route in image-registry-system.

Verify the generated Ingress and Registry configuration:

kubectl get configs.imageregistry.operator.alauda.io cluster -o yaml
kubectl -n image-registry-system get ingress default-route

Get the generated host:

REGISTRY_RULE_HOST="$(
  kubectl -n image-registry-system get ingress default-route \
    -o jsonpath='{.spec.rules[0].host}'
)"

REGISTRY_STATUS_ADDRESS="$(
  kubectl -n image-registry-system get ingress default-route \
    -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'
)"
if [ -z "$REGISTRY_STATUS_ADDRESS" ]; then
  REGISTRY_STATUS_ADDRESS="$(
    kubectl -n image-registry-system get ingress default-route \
      -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
  )"
fi

printf 'Ingress rule host: %s\n' "$REGISTRY_RULE_HOST"
printf 'Ingress status address: %s\n' "$REGISTRY_STATUS_ADDRESS"

Expected results:

  • Config/cluster.spec.defaultRoute is true.
  • The default-route Ingress exists in image-registry-system.
  • A non-empty spec.rules[].host is a usable Registry hostname. An empty rule host means that the Ingress is a catch-all rule; a status address identifies the Ingress controller endpoint and is not automatically a Registry DNS name.

If the default Ingress has no usable hostname, configure a custom secure host instead. Do not use an empty rule host as the value of --registry.

When a usable host is available, assign it to REGISTRY_HOST. If the client uses a private CA, configure that CA before running the TLS check. The check must succeed with the client's normal trust settings before logging in:

REGISTRY_HOST="$REGISTRY_RULE_HOST"

test -n "$REGISTRY_HOST" || {
  echo "No usable Registry hostname was found; configure a custom secure host." >&2
  exit 1
}

openssl s_client \
  -connect "$REGISTRY_HOST:443" \
  -servername "$REGISTRY_HOST" \
  -verify_return_error </dev/null

Write credentials for the generated host:

ac registry login \
  --registry "$REGISTRY_HOST"

Expose a Custom Secure Registry Host

Create or provide a TLS Secret in image-registry-system, then configure Config.spec.routes[].

Create the Secret from a certificate whose Subject Alternative Name matches the custom hostname:

kubectl -n image-registry-system create secret tls registry-tls \
  --cert=/path/to/registry.example.com.crt \
  --key=/path/to/registry.example.com.key

The TLS Secret must exist in image-registry-system when secretName is set. Create a DNS record for registry.example.com that resolves to the Ingress controller.

The generated Ingress uses the registry service as the backend and sets the backend protocol to HTTPS.

Patch Config/cluster with the custom route. Use a merge patch so other Config/cluster.spec fields, such as storage and pull-secret settings, are not removed. The routes array in this patch replaces the current routes array, so include every custom route that must remain configured:

kubectl patch configs.imageregistry.operator.alauda.io cluster \
  --type=merge \
  -p '{
    "spec": {
      "managementState": "Managed",
      "defaultRoute": false,
      "routes": [
        {
          "name": "public-registry",
          "hostname": "registry.example.com",
          "secretName": "registry-tls"
        }
      ]
    }
  }'
ValueDescription
public-registryRoute name. The Operator uses it as the generated Ingress name.
registry.example.comRegistry hostname reachable by external clients. The DNS record must resolve to the Ingress controller.
registry-tlsTLS Secret in image-registry-system. The certificate Subject Alternative Name must match the Registry hostname.

Verify the configuration and generated Ingress:

kubectl get configs.imageregistry.operator.alauda.io cluster -o yaml
kubectl -n image-registry-system get ingress public-registry
kubectl -n image-registry-system describe ingress public-registry

Expected results:

  • Config/cluster.spec.routes[] contains public-registry.
  • The public-registry Ingress uses registry.example.com.
  • The Ingress references the registry-tls Secret.

Configure client trust as described in Configure Client Trust, then verify TLS trust and login:

openssl s_client \
  -connect registry.example.com:443 \
  -servername registry.example.com \
  -verify_return_error </dev/null

ac registry login \
  --registry registry.example.com

Configure Client Trust

If the Ingress certificate is signed by a private CA, add the CA to each external client's trust store before login and push or pull operations.

For a test client that uses an insecure registry option, scope the insecure setting to the registry host only:

ac registry login \
  --registry registry.example.com \
  --insecure

Configure the OCI client's trust or insecure-registry setting separately if that client also connects to an HTTP endpoint or an untrusted certificate.

Verify External Access

Check the Ingress:

kubectl -n image-registry-system get ingress
kubectl -n image-registry-system describe ingress public-registry

Write credentials from an external client:

ac registry login \
  --registry registry.example.com

Push and pull a test image:

nerdctl tag my-app:latest registry.example.com/team-a/my-app:external-test
nerdctl push registry.example.com/team-a/my-app:external-test
nerdctl pull registry.example.com/team-a/my-app:external-test

Troubleshooting

SymptomCheck
Ingress is missingConfig.spec.defaultRoute, Config.spec.routes[], and Operator logs.
Ingress has no addressIngress controller status, ALB project label, and Ingress events.
TLS handshake failsTLS Secret, client trust store, certificate SANs, and private CA configuration.
Login fails credentials, namespace RoleBinding, OIDC runtime discovery, and Registry logs.
Push or pull fails after loginImage namespace permissions, repository path, storage backend, and Registry readiness.