Whoever signs the BI purchase is rarely the person who gets paged about it. This is for the second person: what you are actually agreeing to operate, and what the failure modes look like when they arrive.

What you are running

flowchart LR
    CDN["Static console + site
CDN, no server"] --> API API["One container
1 vCPU / 4 GB"] --> PG[("Postgres
small, transactional")] API --> OBJ[("Object storage
Parquet extracts")] API -.->|"on refresh only"| SRC["Your warehouses"] style API fill:#eef1ff,stroke:#c7cdf2 style OBJ fill:#eef1ff,stroke:#c7cdf2
Three managed things and one container. The query engine is embedded in the process, so there is no cluster to size, patch or rebalance — which is the single biggest operational difference from the tools this replaces.
ComponentWhat it holdsIf it is down
The containerThe API and the embedded query engine.Dashboards do not load. Nothing is lost; restart it.
PostgresModels, dashboards and versions, access policies, audit log.Nothing works, and this is the thing you restore from backup.
Object storageParquet extracts, versioned.Dashboards fail; data is re-creatable by refreshing from source.
Static consoleThe front end.Serve from the CDN's cache, or redeploy a directory of files.

That is the shape of operating this: the alert was worth having, the response was to read one page, and nothing needed doing at 02:14. Reporting is not a tier-one service and building it like one produces pages nobody should have answered.

What to back up

Postgres. That is the answer, and it is small.

The extracts are derived data — if you lost the bucket entirely you would refresh from source and be back, having lost only the history of old versions. The container is an image. The console is a build artefact. The only thing that cannot be regenerated is the metadata: the models people argued over, the dashboards they built, the access policies, and the audit log.

Test the restore, not the backup

The database is small enough that a restore drill takes minutes, which removes every excuse for not doing one. Restore into a scratch environment, point a container at it, and confirm a dashboard loads. If you have never done this, you have a backup and not a recovery plan.

What to watch

Four signals. Anything beyond these is usually noise for a service of this shape.

SignalWhyWhat it usually means
Container health endpointThe whole compute tier is one process.Restart. If it recurs, read the log — it is almost always a dependency or a config value.
Refresh failures, by datasourceA silently failing refresh is a dashboard quietly serving old numbers.The one alert that genuinely matters
Extract storage growthVersions accumulate by design; that is the rollback story.Your lifecycle rule is missing or too generous.
Postgres connections and sizeSmall and transactional. Growth is usually the audit log.Consider a retention policy on the log — and decide it with whoever answers to auditors.

The only alert worth waking someone for is a refresh that has failed repeatedly, because that is the one failure that is invisible to users while actively misleading them.

Refreshes back off on repeated failure rather than retrying in a tight loop, so a source that is down overnight does not produce a thousand log lines and a thousand connection attempts. It produces a datasource in a failed state with its reason attached, which is what someone should see in the morning.

Datasource
A datasource page showing status ready, 600 rows in one table, the extract version and size, and when it expires
The operational page. Status, row count, size, version, and the schedule. When something is wrong with a datasource, the reason is on this page rather than in a log somebody has to go and find.

Upgrades

A new container image and a rolling restart. Schema migrations run at start, and they run forward only — which is a deliberate constraint and worth knowing about.

WHAT MAKES THIS EASYOne process, no cluster

There is no coordinator to drain, no worker fleet to roll, no version skew between components to reason about. The container is the deployment.

WHAT TO WATCH FORSilent rollback

Container orchestrators revert a deployment whose tasks will not start, and some report success while doing it. Check the running revision against the one you registered, not just that the service is stable. We shipped nothing for several hours once because of exactly this.

Scaling, and what actually forces it

One container with 4 GB serves a few hundred readers, because a dashboard panel reads a few megabytes of columnar data and most panels paint from a stored result while fresh numbers are fetched. Two things force a bigger container, and neither is reader count:

  1. Large refreshes. Extracting a wide table streams through the process. This is the memory-hungry operation. If you are sizing for anything, size for your largest refresh.
  2. Live queries against a slow source. The container holds the connection while the warehouse thinks. Concurrency is bounded by the warehouse, not by you.

Adding replicas helps the second and does nothing for the first. Measure which one you have before scaling, because the instinct is backwards here.

1 containerthe entire compute tier
Postgresthe only thing to back up
1 alertrepeated refresh failure
Forward onlyschema migrations, by design

Security questions you will be asked

QuestionAnswer
Where does the data live?Your object storage, your account, Parquet. Nothing is held by a vendor.
What credentials does the service hold?Warehouse connection details, encrypted and never displayed after entry. Object storage uses the platform credential chain — a task role or managed identity, not a key in config.
Does it need inbound internet access?No. The container sits behind a load balancer with no public address.
How is row-level security enforced?Compiled into the query, on every path — dashboard, export, API, AI answer. The mechanism →
What is logged?Every query with its person, row count, duration — including refusals. Refusals are the part auditors ask about.
Can data leave the server?Only if you allow it per datasource, and never for anyone a row or column policy applies to. Mark sensitive datasets server-only.
Access policies
The access policies tab listing a row policy limiting west managers to the West region and a column policy hiding profit and profit ratio from sales reps
Worth knowing even if you do not own it. Rules live on the datasource, so they are inherited by every dashboard built later — which means the governance answer does not degrade as the estate grows.

What we would not claim

  • This is not a high-availability design as deployed. Single region, single container in the reference deployment. Adequate for reporting; if your BI is tier-one, say so during evaluation and plan for more.
  • Migrations are forward-only. A rollback to a previous image across a schema change is not supported. Restore the database if you truly need to go back.
  • No CodePipeline is wired up. The build project exists; deployment runs from CI you provide.
  • Incremental refresh by partition is unfinished. Today a refresh rewrites the extract, which is the thing to know when sizing your largest table.

The honest summary

This is a small service. One process, one database worth backing up, one alert worth configuring. The operational burden is closer to an internal web application than to a BI platform, and that is the point — the complexity that usually lives in a reporting cluster has been moved into a library that runs inside the process.

The reference deployment in detail →  ·  Who owns the access rules →

← All posts   Request a demo