GarageBucket v1beta1¶
API version: garage.rajsingh.info/v1beta1. Scope: Namespaced.
The custom-resource guide explains operational behavior. The JSON schemas are the validation source of truth.
Spec¶
GarageBucketSpec defines the desired state of GarageBucket
| Field path | Type | Constraints | Description |
|---|---|---|---|
bucketId |
string | BucketID pins this resource to a pre-existing Garage bucket ID. When set, the operator will never create a new bucket — it only manages settings and key permissions for the identified bucket. Takes priority over GlobalAlias-based lookup. Useful for importing existing buckets and for recovery after cluster incidents. | |
clusterRef |
object | required | ClusterRef references the GarageCluster this bucket belongs to |
clusterRef.kubeConfigSecretRef |
object | KubeConfigSecretRef is reserved for a future remote Kubernetes client integration. It is currently rejected by admission because the operator does not use it. | |
clusterRef.kubeConfigSecretRef.key |
string | required | The key of the secret to select from. Must be a valid secret key. |
clusterRef.kubeConfigSecretRef.name |
string | default: "" | Name of the referent. This field is effectively required, but due to backwards compatibility is allowed to be empty. Instances of this type with an empty value here are almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names |
clusterRef.kubeConfigSecretRef.optional |
boolean | Specify whether the Secret or its key must be defined | |
clusterRef.name |
string | required | Name of the GarageCluster resource. |
clusterRef.namespace |
string | Namespace of the GarageCluster. Defaults to the referencing resource's namespace. Cross-namespace references require a GarageReferenceGrant where supported by the owning resource. GarageNode and GarageAdminToken reject them. | |
deletionPolicy |
string | default: "Delete"; enum: ["Delete", "Retain"] | DeletionPolicy controls whether deleting this resource also deletes the corresponding Garage bucket. The default is Delete. |
globalAlias |
string | GlobalAlias is the global alias for this bucket (optional) If not set, the bucket name from metadata.name is used | |
keyPermissions |
array of object | KeyPermissions grants access to specific GarageKeys. Note: Permissions can be granted from either direction: - Here (GarageBucket.keyPermissions): Grant keys access to this bucket - On GarageKey (GarageKey.bucketPermissions): Grant the key access to buckets Both approaches are equivalent and result in the same Garage API calls. Use whichever is more convenient for your workflow: - Bucket-centric: Define all key access on the bucket - Key-centric: Define all bucket access on the key If the same permission is defined in both places, they are merged (not conflicting). | |
keyPermissions[].keyRef |
object | required | KeyRef references the GarageKey by name (and optionally namespace). |
keyPermissions[].keyRef.name |
string | required | Name of the GarageKey. |
keyPermissions[].keyRef.namespace |
string | Namespace of the GarageKey. Defaults to the GarageBucket's namespace. Cross-namespace references require a GarageReferenceGrant in the target namespace. | |
keyPermissions[].owner |
boolean | default: false | Owner allows bucket owner operations |
keyPermissions[].read |
boolean | default: false | Read allows reading objects |
keyPermissions[].write |
boolean | default: false | Write allows writing objects |
lifecycle |
object | Lifecycle configures bucket lifecycle policies (object expiration, abort of incomplete multipart uploads). Garage exposes lifecycle only via the S3 API, not the admin API. The operator applies rules using an internal access key it manages per GarageCluster. Garage supports a strict subset of the AWS S3 lifecycle spec: only Expiration (days or date, no ExpiredObjectDeleteMarker) and AbortIncompleteMultipartUpload. Filters support prefix and object size bounds; tag filters and the deprecated rule-level Prefix are not accepted. Garage's lifecycle worker runs daily at midnight (UTC by default), so rule evaluation is asynchronous from reconciliation. | |
lifecycle.rules |
array of object | Rules to apply. The operator replaces the bucket's lifecycle configuration with this exact set on each reconcile. | |
lifecycle.rules[].abortIncompleteMultipartUploadDays |
integer | format: "int32"; min: 1 | AbortIncompleteMultipartUploadDays aborts multipart uploads that have been pending for at least this many days. |
lifecycle.rules[].expirationDate |
string | format: "date-time" | ExpirationDate expires current objects on or after this UTC date. |
lifecycle.rules[].expirationDays |
integer | format: "int32"; min: 1 | ExpirationDays expires current objects this many days after creation. |
lifecycle.rules[].filter |
object | Filter narrows the rule to a subset of objects. If unset, the rule applies to every object in the bucket. | |
lifecycle.rules[].filter.objectSizeGreaterThan |
integer | format: "int64"; min: 0 | ObjectSizeGreaterThan matches objects strictly larger than this many bytes. |
lifecycle.rules[].filter.objectSizeLessThan |
integer | format: "int64"; min: 1 | ObjectSizeLessThan matches objects strictly smaller than this many bytes. |
lifecycle.rules[].filter.prefix |
string | Prefix matches object keys starting with this string. | |
lifecycle.rules[].id |
string | required; min length: 1 | ID is the rule identifier. Must be unique within the bucket. |
lifecycle.rules[].status |
string | default: "Enabled"; enum: ["Enabled", "Disabled"] | Status enables or disables this rule. Disabled rules are sent to Garage but skipped by the lifecycle worker. |
localAliases |
array of object | LocalAliases are per-key local aliases for this bucket | |
localAliases[].alias |
string | required | Alias is the bucket name this key will use to access the bucket. Must be unique within the key's alias namespace. |
localAliases[].keyRef |
string | required | KeyRef is the name of the GarageKey in the same namespace that owns this alias. |
quotas |
object | Quotas configures bucket quotas | |
quotas.maxObjects |
integer | format: "int64" | MaxObjects is the maximum number of objects |
quotas.maxSize |
integer or string | pattern: "^(\+|-)?(([0-9]+(\.[0-9])?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9])?)|(\.[0-9]+))))?$" | MaxSize is the maximum bucket size in bytes |
website |
object | Website configures static website hosting for this bucket. Note: Only indexDocument and errorDocument are supported via the Admin API. For advanced features (routing rules, redirectAll), use S3 PutBucketWebsite API directly. | |
website.enabled |
boolean | Enabled enables static website hosting. | |
website.errorDocument |
string | ErrorDocument is the error document to serve for 404s | |
website.indexDocument |
string | default: "index.html" | IndexDocument is the default index document (default: index.html) |
websiteExposure |
object | WebsiteExposure optionally exposes a website-enabled bucket through Kubernetes HTTP routing: an Ingress or a Gateway API HTTPRoute, created in this bucket's namespace and pointing at the referenced cluster's web API Service. At most one of Ingress and Gateway may be set. Requires spec.website.enabled. See the websiteExposure documentation for the hostname semantics (Garage resolves the bucket from the Host header). | |
websiteExposure.backendRef |
object | BackendRef overrides the Service the exposure routes to. When unset, the operator targets the referenced cluster's web API Service ( |
|
websiteExposure.backendRef.group |
string | max length: 253 | Group of the referent. Defaults to "" (the core API group). |
websiteExposure.backendRef.kind |
string | max length: 253 | Kind of the referent. Defaults to Service. |
websiteExposure.backendRef.name |
string | required; min length: 1; max length: 253 | Name of the referent. |
websiteExposure.backendRef.namespace |
string | min length: 1; max length: 63 | Namespace of the referent. Defaults to the bucket's namespace (the exposure resource's namespace). For an Ingress it must stay the bucket's namespace: Ingress backends cannot cross namespaces. |
websiteExposure.gateway |
object | Gateway configures the generated Gateway API HTTPRoute. Mutually exclusive with Ingress. Requires the Gateway API CRDs to be installed; without them the operator reports a condition and does not fail the bucket. | |
websiteExposure.gateway.annotations |
map of string | Annotations to add to the HTTPRoute. | |
websiteExposure.gateway.labels |
map of string | Labels to add to the HTTPRoute (for example external-dns or argo-rollouts annotations). Operator-managed labels take precedence on conflict. | |
websiteExposure.gateway.parentRefs |
array of object | required; min items: 1 | ParentRefs are passed through verbatim to the HTTPRoute's spec.parentRefs (Gateway names, optional sectionName, and optional cross-namespace references). At least one is required. |
websiteExposure.gateway.parentRefs[].group |
string | default: "gateway.networking.k8s.io"; max length: 253; pattern: "^$|^[a-z0-9]([-a-z0-9][a-z0-9])?(\.[a-z0-9]([-a-z0-9][a-z0-9])?)*$" | Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core |
websiteExposure.gateway.parentRefs[].kind |
string | default: "Gateway"; min length: 1; max length: 63; pattern: "^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$" | Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. |
websiteExposure.gateway.parentRefs[].name |
string | required; min length: 1; max length: 253 | Name is the name of the referent. Support: Core |
websiteExposure.gateway.parentRefs[].namespace |
string | min length: 1; max length: 63; pattern: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$" | Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. |
websiteExposure.gateway.parentRefs[].port |
integer | format: "int32"; min: 1; max: 65535 | Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set Port unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. |
websiteExposure.gateway.parentRefs[].sectionName |
string | min length: 1; max length: 253; pattern: "^[a-z0-9]([-a-z0-9][a-z0-9])?(\.[a-z0-9]([-a-z0-9][a-z0-9])?)*$" | SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core |
websiteExposure.hostnames |
array of string | max items: 16 | Hostnames are the external hostnames the exposure routes on. When empty, the operator uses the single canonical hostname |
websiteExposure.ingress |
object | Ingress configures the generated Kubernetes Ingress. Mutually exclusive with Gateway. Only valid when the bucket and the referenced cluster share a namespace. | |
websiteExposure.ingress.annotations |
map of string | Annotations to add to the Ingress (for example the TLS or proxy-protocol annotations your ingress controller expects). | |
websiteExposure.ingress.ingressClassName |
string | min length: 1; max length: 253 | IngressClassName is the ingress class the Ingress must match (spec.ingressClassName). When empty, the Ingress is left without a class so the cluster's default ingress controller picks it up. |
websiteExposure.ingress.labels |
map of string | Labels to add to the Ingress. Operator-managed labels take precedence on conflict. | |
websiteExposure.ingress.tlsSecretName |
string | min length: 1; max length: 253 | TLSSecretName is the name of a TLS Secret in the bucket's namespace (where the generated Ingress lives), attached to the Ingress (spec.tls). The Secret must already exist; the operator does not provision certificates. |
Status¶
GarageBucketStatus defines the observed state of GarageBucket
| Field path | Type | Constraints | Description |
|---|---|---|---|
bucketId |
string | BucketID is the internal Garage bucket ID | |
conditions |
array of object | Conditions represent the current state | |
conditions[].lastTransitionTime |
string | required; format: "date-time" | lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. |
conditions[].message |
string | required; max length: 32768 | message is a human readable message indicating details about the transition. This may be an empty string. |
conditions[].observedGeneration |
integer | format: "int64"; min: 0 | observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. |
conditions[].reason |
string | required; min length: 1; max length: 1024; pattern: "^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$" | reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. |
conditions[].status |
string | required; enum: ["True", "False", "Unknown"] | status of the condition, one of True, False, Unknown. |
conditions[].type |
string | required; max length: 316; pattern: "^([a-z0-9]([-a-z0-9][a-z0-9])?(\.[a-z0-9]([-a-z0-9][a-z0-9])?)/)?(([A-Za-z0-9][-A-Za-z0-9_.])?[A-Za-z0-9])$" | type of condition in CamelCase or in foo.example.com/CamelCase. |
createdAt |
string | format: "date-time" | CreatedAt is when the bucket was created in Garage |
globalAlias |
string | GlobalAlias is the assigned global alias | |
incompleteUploadBytes |
integer | format: "int64" | IncompleteUploadBytes is the total bytes in incomplete multipart uploads |
incompleteUploadParts |
integer | format: "int64" | IncompleteUploadParts is the count of parts in incomplete multipart uploads |
incompleteUploads |
integer | format: "int64" | IncompleteUploads is the count of incomplete multipart uploads |
keys |
array of object | Keys contains keys with access to this bucket | |
keys[].keyId |
string | KeyID is the access key ID | |
keys[].name |
string | Name is the key name | |
keys[].permissions |
object | Permissions granted to this key | |
keys[].permissions.owner |
boolean | Owner permission | |
keys[].permissions.read |
boolean | Read permission | |
keys[].permissions.write |
boolean | Write permission | |
lifecycleRules |
array of object | LifecycleRules summarises lifecycle rules currently applied to the bucket in Garage. Spec is the source of truth for rule contents; this list reports id and enabled state only. | |
lifecycleRules[].id |
string | required | ID of the rule. |
lifecycleRules[].status |
string | required; enum: ["Enabled", "Disabled"] | Status is Enabled or Disabled. |
localAliases |
array of object | LocalAliases tracks per-key local aliases for this bucket | |
localAliases[].alias |
string | Alias is the local alias name | |
localAliases[].keyId |
string | KeyID is the access key ID that owns this alias | |
localAliases[].keyName |
string | KeyName is the friendly name of the key | |
managedGlobalAlias |
string | ManagedGlobalAlias is the global alias reserved or successfully managed from spec.globalAlias (or the bucket name when spec.globalAlias is empty). It is separate from GlobalAlias, which reports observed Garage state, so aliases created outside the operator are never removed accidentally. | |
managedKeyGrants |
array of string | ManagedKeyGrants lists access key IDs with reserved or active operator ownership from this bucket's spec.keyPermissions. IDs are recorded before the first remote mutation and removed only after exact convergence, allowing crash-safe revocation when a declaration is dropped without disturbing grants managed through GarageKey or by hand. | |
managedLocalAliases |
array of object | ManagedLocalAliases lists the per-key aliases reserved or successfully managed from spec.localAliases. IDs are recorded before the first remote add so an interrupted add can still be removed safely. | |
managedLocalAliases[].alias |
string | Alias is the local alias name | |
managedLocalAliases[].keyId |
string | KeyID is the access key ID that owns this alias | |
managedLocalAliases[].keyName |
string | KeyName is the friendly name of the key | |
observedGeneration |
integer | format: "int64" | ObservedGeneration is the last observed generation |
pendingGlobalAlias |
string | PendingGlobalAlias reserves a replacement before its first remote add. ManagedGlobalAlias retains the old alias until the replacement succeeds, so a failed rename never leaves the bucket without its prior alias. | |
phase |
string | enum: ["Pending", "Creating", "Ready", "Deleting", "Failed", "Unknown"] | Phase represents the current phase |
quotaUsage |
object | QuotaUsage shows current quota consumption | |
quotaUsage.objectCount |
integer | format: "int64" | ObjectCount is the current object count |
quotaUsage.objectLimit |
integer | format: "int64" | ObjectLimit is the configured object limit (0 = unlimited) |
quotaUsage.objectPercent |
integer | format: "int32" | ObjectPercent is the percentage of object quota used |
quotaUsage.sizeBytes |
integer | format: "int64" | SizeBytes is the current size in bytes |
quotaUsage.sizeLimit |
integer | format: "int64" | SizeLimit is the configured size limit in bytes (0 = unlimited) |
quotaUsage.sizePercent |
integer | format: "int32" | SizePercent is the percentage of size quota used |
size |
string | Size is the current bucket size | |
websiteConfig |
object | WebsiteConfig shows the current website configuration details | |
websiteConfig.errorDocument |
string | ErrorDocument is the configured error document | |
websiteConfig.indexDocument |
string | IndexDocument is the configured index document | |
websiteEnabled |
boolean | WebsiteEnabled indicates if website hosting is currently enabled | |
websiteExposure |
object | WebsiteExposure reports the operator-generated HTTP routing resource (Ingress or HTTPRoute) when spec.websiteExposure is set. | |
websiteExposure.hostnames |
array of string | Hostnames are the hostnames the generated resource routes on. | |
websiteExposure.name |
string | Name is the name of the generated resource, in the bucket's namespace. | |
websiteExposure.parents |
array of object | Parents mirrors the route's status.parents for an HTTPRoute: the per-parent Accepted/ResolvedRefs/Ready conditions the Gateway controllers publish. Empty for an Ingress, which has no per-parent readiness model. | |
websiteExposure.parents[].accepted |
boolean | Accepted is true when the parent accepted the route (status.parents[].conditions[Accepted]=True). | |
websiteExposure.parents[].message |
string | Message carries the parent's condition message when not ready. | |
websiteExposure.parents[].parent |
string | Parent is the parent Gateway (or other parent) as namespace/name. | |
websiteExposure.parents[].ready |
boolean | Ready is true when the parent reports the route Ready (status.parents[].conditions[Ready]=True). | |
websiteExposure.parents[].resolvedRefs |
boolean | ResolvedRefs is true when the route's backend references resolved on that parent (status.parents[].conditions[ResolvedRefs]=True). | |
websiteExposure.type |
string | enum: ["Ingress", "HTTPRoute"] | Type is the kind of routing resource the operator manages for this bucket: Ingress or HTTPRoute. |
websiteUrl |
string | WebsiteURL is the computed website URL (if website hosting is enabled) |