
Putting It All Together: A Reference Architecture for a GDPR-Compliant SaaS App
July 9, 2026
Moving Your Database from Amazon RDS to European Providers
August 11, 2026Most web applications store the same handful of things in object storage: user uploads, generated assets, media, logs, and backups. For years the default answer has been Amazon S3, and the S3 API became the standard that nearly every library, framework, and backup tool speaks. That standard is the good news. It means you can keep the S3 developer experience while moving the actual bytes onto European infrastructure, either by running an open source server like MinIO yourself or by using a managed S3-compatible service from a provider such as OVHcloud or Scaleway.
This is a primer for architects who need a concrete replacement for S3 in a European deployment. We’ll cover why the S3 API matters, what your options are, how to set them up, where compatibility is solid, and where the edges are.
Why the S3 API is the thing you’re actually replacing
Object storage is simple at its core. Instead of a filesystem with directories, you have buckets that contain objects. Each object is a blob of bytes with a key, and you read and write it over HTTP. Amazon S3 defined the API that describes those operations – create a bucket, put an object, get an object, list objects, delete, and generate presigned URLs for temporary access.
Because that API is a de facto standard, your application isn’t really tied to Amazon. It’s tied to the S3 protocol. Any storage system that implements the same API can serve the same requests. In practice that means a migration off S3 is often a configuration change, not a rewrite: you point your S3 client at a different endpoint and swap the credentials.
That single fact is what makes a European replacement realistic. You keep the AWS SDK, boto3, or whichever S3 library you already use. You keep the same upload logic across development, staging, and production. What changes is where the data lives – and, if you care about GDPR and data residency, that matters a great deal.
Two routes off S3
You have two broad options, and they solve different problems.
Managed S3-compatible storage. Both OVHcloud and Scaleway offer object storage that speaks the S3 API, hosted in European regions. You get buckets, keys, and an HTTPS endpoint without running any servers yourself. This is the closest analogue to using S3 directly: someone else handles the hardware, durability, and scaling, and you pay for what you store and transfer. For most teams this is the path of least resistance, and it keeps data inside Europe by default.
Self-hosted MinIO. MinIO is a free, open source object storage server that implements the S3 API. You run it on your own machines – a VM at OVHcloud, Scaleway, or Hetzner, or a cluster in your Kubernetes setup. You control the disks, the credentials, and the topology. This gives you the most portability and the tightest control over cost and residency, at the price of operating it yourself.
These aren’t mutually exclusive. A common pattern is MinIO for development and staging so engineers can run a real S3 API locally without touching a cloud account, then managed European object storage in production. Because both speak S3, the application code doesn’t know the difference.
Setting up managed object storage on a European provider
The workflow with OVHcloud or Scaleway is close to what you already know from AWS:
- Create a bucket in a European region. Pick the region closest to your users or the one your compliance requirements dictate.
- Generate access keys – an access key ID and a secret access key. Treat these like account credentials.
- Note the endpoint URL for that region. Unlike AWS, where the endpoint is implicit in the SDK, you’ll usually set an explicit endpoint pointing at the provider’s regional S3 service.
- Point your application at the endpoint and keys, and confirm it can list, put, and get objects.
The main configuration difference from vanilla AWS usage is that you set the endpoint yourself. In boto3, for example, that means passing endpoint_url and the region name when you create the client, alongside the credentials. Everything downstream – uploads, downloads, presigned URLs – uses the same calls you’d make against Amazon.
Running MinIO yourself
MinIO is straightforward to stand up and deliberately unopinionated about where it runs. A single-container deployment is enough to get a working S3 API:
- Run the server as a Docker container or a binary, giving it a data directory to store objects and root credentials (an access key and secret key).
- Know your two endpoints. MinIO serves the S3 API on one port and a browser console on another. Applications talk to the API; you use the console to create buckets and manage access.
- Put HTTPS in front. Object storage endpoints carry credentials and large transfers, so terminate TLS with a reverse proxy such as Ingress-NGINX plus cert-manager on Kubernetes, or a proxy on a plain VM. Do not expose the API over plain HTTP.
- Size the disk for growth. MinIO’s capacity is its volume’s capacity. Monitor free space, because a full volume fails writes.
For anything beyond development, don’t put a single MinIO instance in the critical path. MinIO’s distributed mode spreads data across multiple drives and nodes using erasure coding, which lets it tolerate disk and node failures while presenting one S3 endpoint. That’s a bigger topic than a single-container deploy, but it’s the difference between a convenient dev tool and a production store you can trust with user data.
Credential hygiene that matters more with storage
A few habits are worth building in from the start:
- Never hand root keys to applications. Create scoped access keys limited to the specific buckets an app needs. A leaked scoped key is a contained problem; a leaked root key is not.
- Keep buckets private by default. Make objects public deliberately, per bucket, only when you intend to serve them openly.
- Give each application its own bucket and its own scoped key so their data and permissions stay separate on shared storage.
Where compatibility holds, and where it doesn’t
For the operations most applications actually use – buckets, put, get, list, delete, multipart upload, and presigned URLs – both MinIO and the managed European services behave like S3. That covers the large majority of real-world usage, and it’s why endpoint-swap migrations work as often as they do.
The edges are worth checking before you commit. A handful of Amazon-specific features have no exact equivalent everywhere, so if your application depends on advanced S3 behavior – certain lifecycle transition tiers, specific object-lock or versioning semantics, S3 Select, or deep IAM policy conditions – verify each one against the target platform rather than assuming parity. Feature support also differs between MinIO and the managed providers, so treat “S3-compatible” as a starting point to test, not a guarantee of every capability.
One practical gotcha: addressing style. Amazon uses virtual-hosted-style URLs (bucket name in the hostname), while some S3-compatible endpoints expect path-style addressing (bucket name in the path). If a client authenticates fine but can’t find your bucket, switching to path-style is usually the fix. Most SDKs expose this as a single configuration flag.
Make the migration reversible
The safest cutover is one you can undo. Because everything speaks S3, you can copy objects between endpoints with standard tooling – mc mirror from MinIO’s client, rclone, or the AWS CLI pointed at each endpoint in turn. A sensible sequence:
- Stand up the target bucket on your European provider or MinIO cluster and run compatibility tests against it with a copy of real workloads.
- Mirror existing objects across, then keep them in sync while both endpoints are live.
- Switch the application’s endpoint configuration and verify uploads, downloads, and presigned URLs in production.
- Keep the old bucket readable for a defined window so you can roll back by flipping the endpoint value if something surfaces.
The preparation is the boring part, and it should be done first. Do it well and the actual switch is a config change with a rollback path.
Choosing between managed and self-hosted
Reach for managed European object storage when you want S3 semantics with minimal operational burden, predictable durability handled by the provider, and data that stays in a European region. It’s the natural fit for most web applications and the fastest way to leave Amazon without changing how your code works.
Run MinIO when you need maximum portability – the same open source server on any provider or on-premises – tight control over where every byte sits, or a real S3 API in development and CI. It rewards teams that are comfortable operating a stateful service and want no dependency on any single vendor.
Either way, the S3 API is what keeps your options open. That’s the point of building on an open standard: your storage layer becomes something you can move, not something that pins you to one provider. If you’re mapping out which hyperscaler components to replace with European, open source-friendly alternatives, object storage is one of the cleaner swaps to start with – and a good proof point for the rest of the stack.
