Key takeaways
- provider-ovh 2.23.0 moves the upstream OVHcloud Terraform provider from 2.19.0 to 2.23.0, which covers four upstream releases, and adds 16 managed resources in both the cluster and namespaced API groups.
- The biggest addition is the new cloud load balancer API: five kinds that reference each other, so you can declare a whole load balancer in a single manifest.
- No breaking changes for manifests. Only Go code that imports the generated types needs updating, for two renames in the
cloudgroup.
Version 2.23.0 of provider-ovh, our Crossplane provider for OVHcloud, was released on 26 October 2026. It moves from OVHcloud Terraform provider 2.19.0 to 2.23.0 and brings the provider to 184 managed resources per API scope and 373 CRDs, up from 168 and 341.
2.19.1 changed how the provider talks to OVHcloud and kept the resource list the same. 2.23.0 does the opposite: the architecture stays the same and the list grows, which is why it comes with no migration section.
What’s new: 16 resources, in both API scopes
Each new kind is generated in the cluster-scoped *.ovh.edixos.io groups and the namespaced *.ovh.m.edixos.io groups.
| API group | Kind | Upstream resource | Upstream release |
|---|---|---|---|
cloud | InstanceSnapshot | ovh_cloud_instance_snapshot | 2.20.0 |
privatesql | PrivatedatabaseWebhostingNetwork | ovh_hosting_privatedatabase_webhosting_network | 2.20.0 |
cloud | Loadbalancer, LoadbalancerListener, LoadbalancerPool, LoadbalancerPoolMember, LoadbalancerL7Policy | ovh_cloud_loadbalancer* | 2.21.0 |
dedicatedserver | ServerVirtualMac | ovh_dedicated_server_virtual_mac | 2.21.0 |
storage | ObjectBucket | ovh_cloud_storage_object_bucket | 2.22.0 |
clouddiskarray | CephPool, CephUser | ovh_dedicated_ceph_pool, ovh_dedicated_ceph_user | 2.23.0 |
email | DomainDKIM, DomainRedirection | ovh_email_domain_dkim, ovh_email_domain_redirection | 2.23.0 |
iam | IAMLogSubscription | ovh_iam_log_subscription | 2.23.0 |
me | APILogSubscription, AuditLogSubscription | ovh_me_api_log_subscription, ovh_me_logs_audit_log_subscription | 2.23.0 |
All of them use the ID the OVHcloud API returns as their external-name.
The new load balancer API, as one manifest
The five Loadbalancer* kinds wrap the load balancer resources upstream added in 2.21.0. The useful part on the Crossplane side is the references. Upstream resources link to each other by ID, and provider-ovh 2.23.0 lets each of those IDs be resolved by reference or by selector:
loadbalancerIdon the listener, pool and pool member resolves to aLoadbalancer.poolIdon a pool member, anddefaultPoolIdon a listener, resolve to aLoadbalancerPool.listenerIdandredirectPoolIdon an L7 policy resolve to a listener and a pool.
So you no longer copy a load balancer ID out of status.atProvider and paste it into the next object. Crossplane waits for the load balancer to report an ID, fills it in, and moves on:
apiVersion: cloud.ovh.m.edixos.io/v1alpha1
kind: Loadbalancer
metadata:
name: web
namespace: team-a
spec:
forProvider:
serviceName: <public cloud project ID>
region: GRA11
name: web
flavorName: small
network:
id: <private network ID>
subnetId: <subnet ID>
---
apiVersion: cloud.ovh.m.edixos.io/v1alpha1
kind: LoadbalancerPool
metadata:
name: web
namespace: team-a
spec:
forProvider:
serviceName: <public cloud project ID>
loadbalancerIdRef:
name: web
name: web
protocol: HTTP
algorithm: ROUND_ROBIN
healthMonitor:
type: HTTP
urlPath: /healthz
delay: 5
timeout: 3
---
apiVersion: cloud.ovh.m.edixos.io/v1alpha1
kind: LoadbalancerPoolMember
metadata:
name: web-0
namespace: team-a
spec:
forProvider:
serviceName: <public cloud project ID>
loadbalancerIdRef:
name: web
poolIdRef:
name: web
address: 10.0.0.10
protocolPort: 8080
---
apiVersion: cloud.ovh.m.edixos.io/v1alpha1
kind: LoadbalancerListener
metadata:
name: web-http
namespace: team-a
spec:
forProvider:
serviceName: <public cloud project ID>
loadbalancerIdRef:
name: web
defaultPoolIdRef:
name: web
name: web-http
protocol: HTTP
protocolPort: 80
Four objects, one kubectl apply, and no IDs written by hand apart from the network you attach the VIP to. That is the form you want before you wrap a load balancer in a composition and hand it to application teams, which is the kind of boundary we design in platform engineering work.
network.ip is optional. Inside the subnet CIDR it pins the VIP’s fixed address. Outside it, it must be an existing floating IP to associate.
This is not a replacement of ProjectLoadbalancer. The older kind in the lb group wraps a different upstream resource, keeps its own schema and is untouched. Nothing moves unless you move it. As with Instance and ProjectInstance in 2.19.0, provider-ovh now exposes both OVHcloud APIs as separate CRDs, and the same applies to InstanceSnapshot next to the older ProjectInstanceSnapshot.
The other new kinds
InstanceSnapshot snapshots an Instance, and its instanceId resolves by reference or selector. As of upstream 2.23.0 it reports the snapshot’s osType (LINUX, WINDOWS or BAREMETAL_LINUX), inherited from the image the instance was created from.
ObjectBucket in the storage group manages an Object Storage bucket directly. Upstream 2.23.0 also fixed tag removal on it: removing a tag from the spec now deletes it on the bucket.
CephPool and CephUser in clouddiskarray manage pools and users on a Cloud Disk Array. They sit next to the existing CephACL kind.
DomainDKIM and DomainRedirection in email manage DKIM signing and redirections on an OVHcloud email domain. When the DNS zone is hosted elsewhere, DomainDKIM reports the selector CNAMEs in status.atProvider so you can publish them, and DKIM stays in toConfigure until you do.
IAMLogSubscription, APILogSubscription and AuditLogSubscription route IAM, API call and account audit logs to a Logs Data Platform stream. For anyone who has to show an auditor where account activity goes, a subscription that lives in Git and gets reconciled is a better answer than a setting someone clicked once.
ServerVirtualMac assigns a virtual MAC to an additional IP on a dedicated server, which is what you need to bridge a VM onto an additional IP. PrivatedatabaseWebhostingNetwork controls whether the OVHcloud web hosting network may connect to a private SQL database.
Fixes that came with the upstream bump
Moving four upstream releases also brings in their fixes and the optional fields they added. Two stand out.
Instance updates no longer detach networks
Upstream 2.21.0 fixed a serious bug in ovh_cloud_instance updates (#1451). The OVHcloud API treats a key that is missing from an update request as empty. The provider used to send partial payloads, so an update could detach networks, volumes and shares, and reset securityGroupIds to a deny-all port.
Instance has been in provider-ovh since 2.19.0. If you run it on 2.19.x and update instances, that is reason enough to upgrade. 2.23.0 sends every mutable field on every update.
User survives a restart
The second fix is ours. After 2.19.1 removed the Terraform CLI, the cloud User kind’s service_name/user_id external-name stopped resolving: imported users never became ready, and created users broke after a provider restart. 2.23.0 uses the bare user ID as the external-name and normalises the old format, so existing objects keep working without any change on your side.
New fields worth knowing
BlockVolume:encryption.kms, to encrypt a volume with a customer-managed key.FileShare:encryptionat rest, andcreateFrom.snapshotIdto create a share from a snapshot.shareTypeandsizeare no longer required, since a share created from a snapshot takes them from the source.Instance:userData, to pass cloud-init at creation.Cluster(kube):disablePublicNetworking.Oauth2Client:discardClientSecret, to keep the client secret out of state for service accounts that never use it.FirewallRule:destinationPortRangeandsourcePortRange.ServerReinstallTask:customizations.appToInstallandstorage.erase.
Behaviour also improves on existing kinds. ProjectDatabase switches networkId and subnetId in place instead of recreating the cluster, and it no longer shows a permanent diff on advancedConfiguration. Kafka topic settings update in place, except that lowering partitions still replaces the topic.
Breaking changes: none for manifests
We compared the generated provider schema with 2.19.0 field by field. That found no removed resources, no removed fields, no type changes other than new nested attributes, no newly required fields and no state schema version bumps. At the CRD level, the only validation rules removed are the ones that required FileShare shareType and size, so the schema got looser, not stricter.
Go API only. To keep the new kinds distinct, upjet renamed two generated types in the cloud group:
| Kind | Before | After |
|---|---|---|
ProjectStorage | RulesParameters | ReplicationRulesParameters |
Quota | LoadbalancerParameters | AvailableProfilesLoadbalancerParameters |
LoadbalancerParameters now belongs to the new Loadbalancer kind. JSON field names are unchanged, so manifests and stored objects are unaffected. This matters only if you import apis/{cluster,namespaced}/... directly, in a composition function, an operator or a test harness. If you do, the compiler will show you both places.
Toolchain: one upstream bump, two toolchain bumps
One thing for anyone maintaining their own upjet provider. Upstream 2.23.0 requires Go 1.27.3, so provider-ovh moved from Go 1.25.13 to 1.27.3. golangci-lint 2.5.0, built with Go 1.25, refuses to lint a Go 1.27 module, so it moved to 2.15.0 as well. The newer goconst linter then flagged seven repeated strings in existing code, mostly ovh_* resource names, which we now exclude in .golangci.yml.
The lesson: before you plan an upstream bump, check the go directive in the upstream go.mod first. A minor upstream release can force a toolchain upgrade, and that one can force another.
None of this changes the image you run. It is still the single Go binary on Alpine that 2.19.1 introduced.
Upgrading
cat <<EOF | kubectl apply -f -
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-ovh
spec:
package: xpkg.upbound.io/edixos/provider-ovh:v2.23.0
EOF
There is no migration to prepare. The new CRDs install alongside the existing ones, and existing managed objects keep reconciling. If you are upgrading from 2.19.0 or earlier, read 2.19.1 first: its migration section still applies, and one of its changes rejects existing vrack CloudProject objects.
Full changelog: v2.19.1…v2.23.0 · release notes
Thanks
@Alegrowin contributed the User external-name fix in #68, tested on their own production clusters. It is their second contribution after #52, and it closes a regression that only shows up after a restart. Thank you.
Thanks also to the OVHcloud team and contributors behind the four upstream releases this one is built on.
If you run provider-ovh, issues and pull requests are the fastest way to shape where it goes next.
Where to go next
New to the provider? The step-by-step provider-ovh guide covers installation, credentials and a first managed cluster. Coming from an older version? Read 2.19.1, then 2.18.0 and 2.17.0, which remove fields.
We maintain provider-ovh and build OVHcloud control planes on it: Crossplane consulting · platform engineering · talk to us.
Straight answers
Frequently asked questions
What is new in provider-ovh 2.23.0?
It tracks the OVHcloud Terraform provider 2.23.0 instead of 2.19.0 and adds 16 managed resources in both API scopes: the new cloud load balancer API (five kinds), InstanceSnapshot, ObjectBucket, Ceph pools and users, a dedicated server virtual MAC, email DKIM and redirections, three log subscriptions and a private database web hosting network. The provider now has 184 managed resources per scope and 373 CRDs.
Is provider-ovh 2.23.0 a breaking release?
Not for manifests. No resource or field was removed between upstream 2.19.0 and 2.23.0, no field became required, and no resource bumped its state schema version. Only Go code importing the generated apis packages is affected, by two type renames in the cloud API group.
Which Go types were renamed in provider-ovh 2.23.0?
In the cloud API group, ProjectStorage's RulesParameters becomes ReplicationRulesParameters, and Quota's LoadbalancerParameters becomes AvailableProfilesLoadbalancerParameters, because LoadbalancerParameters now belongs to the new Loadbalancer kind. JSON field names are unchanged, so CRDs and stored objects are unaffected.
What is the difference between Loadbalancer and ProjectLoadbalancer in provider-ovh?
Loadbalancer, in the cloud API group, wraps the new upstream ovh_cloud_loadbalancer resource and comes with listener, pool, pool member and L7 policy kinds that reference each other. ProjectLoadbalancer, in the lb API group, wraps the older resource and is unchanged. They are separate CRDs with separate schemas.
Why should I upgrade if I use the Instance kind?
Upstream 2.21.0 fixed instance updates. The OVHcloud API treats an absent key in an update as empty, and partial payloads detached networks, volumes and shares and reset the security groups to a deny-all port. Instance has been in provider-ovh since 2.19.0, so any Instance you update on 2.19.x is exposed.