A self-hosted RAG application, stood up on a European cloud from nothing, verified end to end, and torn down again — six times, for about two and a quarter euros.
Scaleway is a European cloud provider — French-owned, operating data centres in Paris, Amsterdam and Warsaw under EU jurisdiction. For organisations whose sovereignty requirements demand a European infrastructure provider, that makes it one of the few alternatives to hosting everything themselves. Self-hosting is the safe answer to those requirements and also the expensive one: hardware, patching, backups, and someone on call. The question this proof of concept asks is whether a sovereign cloud can carry that load instead. It tests three things: how far deployment can be automated, how billing behaves, and how easy the platform is to work with.
The application under test is LocalChat — a self-hosted RAG application that had only ever run on a workstation and in Docker. Running it there says nothing about running it on someone else's infrastructure. So the test is not whether it can be made to run on Scaleway once, by hand, but whether the entire stack can be deployed automatically, from nothing, by scripts, and taken down the same way.
The answer is yes, with conditions. The stack — application container, PostgreSQL with pgvector, and a model host on a private network — has been built from an empty project and destroyed again six times, each cycle scripted end to end, for about two and a quarter euros in total. What it needed to get there:
error before
settling, a network that refuses deletion until its attachments have quietly
released.Idle costs little: the container is pinned to one instance, and the cheapest database option scales to zero — though the current version of the application needs the managed one, which bills by the hour (see below). Nothing in the product was rewritten to fit the platform. The cheap part of the stack — everything but a GPU — is cents per hour, so a mistake costs a rebuild, not a bill. And what the scripts encode is now reusable: the same commands, with prefixed and defaulted settings, would stand the stack up in another account.
Nothing stops spend automatically; a budget alert lags the invoice, so the brake is discipline plus a script. Encryption to the database is structural — an unencrypted connection cannot even name its target. The managed environment exposes assumptions a workstation covers: a health check that never checked, a retrieval path that silently stores nothing without its model host. Diagnosis on a managed instance needs preparation — console and key — before the failure, not after. And the one expensive component, a GPU, was scarce on the day it was checked; a plan that assumes it is available on demand is a plan that waits.
The rest of this page is the summary of those cycles. The two pages behind it are the evidence: the log of every session, and the scripts exactly as they run.
LocalChat lets people upload documents and ask questions about them, with retrieval over a vector database and a locally hosted language model. This proof of concept answers one question: can it run on managed cloud infrastructure without changing what the product is?
It can. The stack is a Serverless Container running the application, a managed PostgreSQL database with pgvector for retrieval, and a small Instance serving the models over a private network. No component was rewritten to fit the platform.
The first five cycles used Scaleway's Serverless SQL Database. The sixth, on 6 October, found that the current version cannot run on it any more: the application now enforces workspace isolation inside the database, which needs a database role that the serverless offering does not allow its users to create. Scaleway's managed PostgreSQL allows it — and now supports pgvector, which it did not when the serverless option was chosen.
| Component | Service | Notes |
|---|---|---|
| Application | Serverless Container | 3 GB, one instance, no autoscaling |
| Database | Managed PostgreSQL 16 (DB-DEV-S) | pgvector 0.8.2, bills hourly. Serverless SQL served cycles one to five |
| Embeddings | CPU Instance running Ollama | Reachable only over a private network |
| Text generation | CPU Instance, same host as embeddings | Works, slowly: a 1B model answers in 24–36 s. A GPU is the open decision |
Four commands. All of them are idempotent: running one twice does nothing the second time. They were written after deploying once by hand, so they encode what actually worked rather than what the documentation predicted.
bash scripts/scaleway/provision.sh # project, database, scoped credentials
bash scripts/scaleway/deploy_container.sh # the application
python scripts/scaleway/verify_deployment.py <endpoint>
bash scripts/scaleway/deploy_embeddings.sh # the embedding model host
Every step talks to Scaleway through its official command-line tool, the
Scaleway CLI (scw):
no console clicks and no Terraform state to keep. A script that is just CLI calls can be
read top to bottom as the procedure it performs, and run against a fake scw in
the test suite, which is how its decisions are checked without a live account. The
scripts are in the public repository, under
scripts/scaleway.
The third command is a gate rather than a checklist. It signs in, confirms the deployed image is the version you think it is, ingests a document and retrieves it with a query that shares no words with the text — so a passing result can only come from the vector search actually working. It exits non-zero on failure.
The platform has no spend cap at any level, so the teardown is a script rather than a habit:
PROJECT_ID=<project> bash scripts/scaleway/panic_teardown.sh # shows what would go
PROJECT_ID=<project> CONFIRM=DESTROY bash scripts/scaleway/panic_teardown.sh # stops it
It deletes rather than stops, because a stopped machine keeps billing for its disks and its reserved address. It refuses to touch anything outside the project it was given.
Four things the plan believed turned out to be wrong, and finding them is most of the value of running a proof of concept at all.
The plan assumed a first pass without any language model would still validate document upload and search. It does not: every document chunk is embedded through the model host, so with none reachable nothing is stored at all. The fix was not an expensive GPU — embeddings alone run acceptably on a small CPU machine, at roughly two cents an hour.
The managed database selects which database you reach from the encrypted handshake itself. An unencrypted connection cannot name its target, so it fails with a confusing error rather than a security warning. The application had never made this setting explicit; it does now.
The application's health endpoint reported the database as healthy for the life of the process, because it echoed a flag recorded at startup rather than asking the database anything. On a server that is always running, this behaves like correct code indefinitely. On a database that scales to zero, it reports green while every request fails.
This was a latent defect, not a platform requirement — the cloud only made it visible. That is the most transferable lesson here: when moving to managed infrastructure, look for the assumptions an always-present environment was quietly covering.
Workspace isolation inside the database switches every request into a restricted role. On a workstation and in the test pipeline the application connects as the database's administrator, who may switch into any role without being granted it — so every test passed. On the managed database the application is an ordinary user, and the grant that should have let it switch did not. Found by deploying, fixed the same day; the same lesson as the health check, one layer further down.
About two and a quarter euros for the entire exercise — database, application container, model host, private networking, stood up and torn down six times. The sixth cycle, on a managed database, cost 24 cents. Most of it went to one stack that was left standing for two days between sessions. The figure matters less than the shape behind it:
The sequencing that made this cheap: prove the plumbing on a two-cent machine, so that when an expensive one is finally switched on it has exactly one question left to answer.
The document path — upload, embed, store, retrieve — works on this infrastructure and has been verified on every cycle. Text generation has been run on the CPU host: the answer is correct and cites the source, but at half a minute to the first word. On 6 October the stack was handed over for hands-on testing, and that settled it: on a CPU it is too slow to test the product properly.
The next cycle runs on a GPU. A GPU instance beside the CPU host, at €0.787 per hour while it runs, built for a two-hour session, measured — under five seconds to the first word, with an answer that uses its source — and torn down. Before it, the provisioning script moves from the serverless database to the managed one. The alternative, a hosted model API, is cheaper to run and a change to what the product is rather than to how it is deployed.
The stack is currently torn down. It rebuilds from the four commands above.
This page is the summary. The two behind it are the evidence: every session's log entry as kept in the repository, and the scripts exactly as they run — written with prefixed, defaulted settings and no account-specific values, so they can be adapted to another account or another project without rewriting them. All of it — the application, the scripts, their tests and the full deployment guide — is public at github.com/jwvanderstam/LocalChat.