Real system design interview questions for DevOps and platform engineers, with detailed answers on high availability, CI/CD, observability and secrets.
60questions with answers
Use a repeatable sequence: clarify the requirements, size the problem, draw the simplest design that works, then spend most of your time on failure modes and trade-offs. That last part is what a DevOps interviewer is really scoring.
The common wrong answer is naming tools before requirements ("I would use Kubernetes and Kafka"). A design with no stated requirement cannot be judged right or wrong, so it signals that you pattern-match instead of reason.
Functional requirements say what the system does. Non-functional requirements say how well it must do it: availability, latency, durability, security, cost and operability.
"Users can upload photos" is functional. "99.9% of uploads succeed within 2 seconds and survive the loss of one Availability Zone" is non-functional. The second sentence is the one that decides how many instances you run, where the data lives and how you alert.
DevOps and platform engineers own most of the non-functional side, so these are the numbers to pin down in the first two minutes of any design question. The trap is listing features and never asking "how reliable does this need to be?", which leaves you designing for an unknown target.
Each extra nine cuts the allowed downtime by ten times, and the cost of reaching it climbs much faster than that.
| Availability | Downtime per year | Downtime per month |
|---|---|---|
| 99% | about 3.65 days | about 7.3 hours |
| 99.9% | about 8.8 hours | about 44 minutes |
| 99.99% | about 53 minutes | about 4.4 minutes |
| 99.999% | about 5 minutes | about 26 seconds |
At 99.9% you can often get by with multi-AZ and a human-led recovery. At 99.99% the whole monthly budget is about four minutes, which is shorter than the time it takes to get paged, log in and diagnose. Recovery has to be automatic.
Dependencies in series multiply. Two components that are each 99.9% available give you about 99.8% together, so a chain of five services cannot promise more than its weakest links allow.
Vertical scaling makes one machine bigger. Horizontal scaling adds more machines and spreads the work across them.
Vertical is simple (no code change) but has a ceiling, usually needs a restart, and leaves you with one big single point of failure. Horizontal has no practical ceiling and gives you redundancy for free, but the application must tolerate running as many copies, which usually means being stateless.
A good default is to scale stateless tiers horizontally and databases vertically first, then add read replicas, and only shard when you must. The common wrong answer is "always scale horizontally". Databases and anything with strong consistency needs make that much harder than adding instances.
A Layer 4 load balancer routes by IP address and port without reading the request. A Layer 7 load balancer understands HTTP, so it can route by path, host or header.
/api and /static to different backends, do canary splits by header or weight, rewrite requests and apply rate limits.In an interview, pick by need. A public web application usually wants L7 for routing and TLS. A database proxy or a gaming backend may want L4. The trap is forgetting that the load balancer itself must not be a single point of failure, so say that it runs across multiple AZs.
A stateless service keeps nothing important in its own memory or disk between requests, so any instance can serve any request and any instance can be thrown away.
That gives you three things at once. A load balancer can send traffic anywhere. Autoscaling can add or remove instances freely. A crashed instance is replaced without losing user data.
State does not disappear, it moves. Put sessions in a shared cache or in a signed token, data in a database, and files in object storage. The wrong answer is "stateless means no state". It means the state lives somewhere designed to hold it, and that place now needs its own redundancy and backup plan.
Latency is how long one request takes. Throughput is how many requests the system completes per second. You can have high throughput with bad latency, for example a deep queue that processes a lot but makes each item wait.
Averages hide pain. If 1% of requests take 5 seconds, the average still looks fine, but one in a hundred users is suffering, and a page that makes 20 calls will hit that slow path in about one load out of five. That is why teams track p95 and p99.
Set SLOs on percentiles, for example "99% of requests under 300 ms". The trap is quoting average latency in a design discussion, which tells the interviewer you have not run a system under real load.
Walk the diagram box by box and ask one question: "if this dies, does a user notice?" Any box where the answer is yes and there is only one of it is a single point of failure (SPOF).
The obvious ones are a single database or a single load balancer. The ones people miss are shared and hidden:
Fix it with redundancy plus a tested failover, not redundancy alone. A standby that has never taken traffic is a hope, not a fix.
RTO (Recovery Time Objective) is how long the business can tolerate being down. RPO (Recovery Point Objective) is how much recent data it can afford to lose.
They are business numbers, and they pick your architecture. A 24-hour RTO and RPO is satisfied by nightly backups. A 5-minute RTO and 1-minute RPO needs a warm standby with continuous replication. Near-zero for both needs active-active, which costs far more and is far harder to run.
State them first in any design. The trap is choosing a recovery strategy by what sounds impressive instead of by what the stated RTO and RPO actually require.
Add a cache where the same data is read many times and changes slowly. Common layers are the CDN (static and cacheable responses), the application (in-memory or Redis), and the database (query cache or read replicas).
In cache-aside, the application checks the cache first. On a hit it returns the value. On a miss it reads the database, writes the result to the cache with a TTL, then returns it. Only data that is actually requested gets cached.
def get_user(user_id): key = f"user:{user_id}" cached = cache.get(key) if cached: return cached # cache hit user = db.fetch_user(user_id) # cache miss cache.set(key, user, ttl=300) # expire after 5 minutes return userThe hard part is invalidation. A TTL gives bounded staleness for free, and deleting the key on write keeps it fresher. The trap is caching data that must be exactly right, such as account balances.
Keep recent logs on fast, expensive storage and move older logs to cheaper tiers, because the value of a log drops quickly with age.
Set the periods from real needs: how far back do incidents get investigated, and what does compliance require? Automate the movement with lifecycle policies so nobody does it by hand. The trap is keeping everything hot forever, which makes logging one of the largest items on the cloud bill.
A liveness probe asks "is this process stuck?" and restarts the container if it fails. A readiness probe asks "can it take traffic right now?" and removes the pod from the Service endpoints if it fails, without restarting it. A startup probe gives slow-starting apps extra time before the other two begin.
They matter because traffic should only reach instances that can serve it. A pod that is still warming its cache should be unready, not dead.
The classic mistake is making liveness depend on a downstream service such as the database. When the database blips, every pod fails liveness and restarts together, turning a small incident into a full outage. Keep liveness shallow and put dependency checks in readiness, carefully.
An operation is idempotent if doing it twice has the same effect as doing it once.
Networks and queues deliver messages more than once, and clients retry on timeouts. If "charge the customer" is not idempotent, a retry charges them twice. The usual fix is an idempotency key: the client sends a unique ID, and the server remembers it and returns the original result on a repeat.
The same idea applies to infrastructure. terraform apply and a well-written deploy script should be safe to run again after a half-finished attempt. The trap is assuming exactly-once delivery exists. Design for at-least-once delivery and make the handler idempotent.
In a queue, each message is handled by exactly one consumer, so it spreads work across workers. In publish-subscribe, each message is delivered to every subscriber, so it spreads events to many interested parties.
Use a queue for work distribution, such as "resize this image". Use pub/sub for fan-out, such as "an order was placed" that billing, shipping and email all need to hear.
Many real systems combine them: an SNS topic fans out to one SQS queue per consumer, so each consumer gets every event but processes at its own pace. The trap is mixing them up and expecting two workers on a queue to each see every message.
Blast radius is how much of the system, or how many users, one failure or one bad change can affect. Good design keeps it small.
The main levers are isolation and staging:
When an interviewer asks "what happens if this goes wrong?", the best answer is a number: "about 5% of users, for about 3 minutes". The trap is a design where one config change, one shared database or one global control plane can take down everything.
Turn the product numbers into requests per second, then into instances and storage. Round aggressively. The goal is the right order of magnitude, not precision.
A worked example: 10 million daily users making 20 requests each is 200 million requests a day. Divide by 86,400 seconds and you get about 2,300 requests per second on average. Peaks are often 3 to 5 times higher, so plan for roughly 10,000 requests per second. If one instance handles about 500 per second, that is 20 instances, plus headroom so you survive losing an AZ.
For storage, multiply record size by volume: 1 KB per request across 200 million requests is about 200 GB a day.
State your assumptions out loud and invite correction. The trap is giving a precise-looking number with no assumptions behind it.
CAP says that when a network partition happens, a distributed data store must choose between consistency (every read sees the latest write) and availability (every request gets an answer). Partitions are a fact of life, so the real choice is between C and A during a failure.
A bank ledger should favour consistency and refuse a write rather than show a wrong balance. A product catalogue or a social feed can favour availability and show slightly stale data. When there is no partition, the trade-off is usually latency versus consistency, which PACELC captures.
The common wrong answer is "pick any two of three", or claiming a system is "CA". If the network can partition, and it can, you do not get to opt out of the choice.
Spread every tier across at least two, ideally three, Availability Zones, keep the application stateless, and make the data tier redundant with automatic failover.
Key decisions to say out loud:
The trap is saying "two AZs" and stopping, without checking that the survivors can actually carry the load.
Go multi-region only when the business needs to survive the loss of a whole region, serve users from nearby for latency, or keep data in specific countries. For most workloads, multi-AZ plus tested cross-region backups is enough.
Multi-AZ already covers a data center failure. A full regional outage is rare, and the cost of defending against it is high: data replication across regions, doubled infrastructure, harder deployments and far more things to test.
Ask what a regional outage actually costs the business per hour, and compare it with the yearly cost of the extra region. If the answer is "a few hours of downtime every few years is acceptable", then warm backups in a second region are the right level. The trap is proposing active-active multi-region for an internal tool with a 24-hour RTO.
Use health-checked DNS records: the DNS service probes the primary endpoint, and if it fails repeatedly it starts answering with the secondary region's address.
What can go wrong:
Use a low TTL (for example 60 seconds) on failover records, test the failover regularly, and keep DNS itself off the critical recovery path where you can.
Graceful degradation means the system keeps its core function when a part fails, by dropping the non-essential parts. Load shedding means deliberately rejecting some requests so the rest can succeed when demand exceeds capacity.
Examples of degradation: show cached or stale product data when the pricing service is down, hide the recommendations widget, accept orders and queue the confirmation emails. Examples of shedding: return 503 with a Retry-After header to low-priority traffic such as bulk exports, while keeping checkout fully served.
Both need to be designed before the incident. Decide which features are critical, wire them to feature flags or automatic limits, and test them. The trap is an all-or-nothing design in which any slow dependency makes the whole page fail.
Timeouts stop a caller waiting forever. Retries recover from brief faults. Circuit breakers stop calling a service that is clearly failing, so it can recover and so callers fail fast.
The rules that keep them safe:
import random, time def call_with_backoff(fn, attempts=4, base=0.2): for n in range(attempts): try: return fn() except TransientError: if n == attempts - 1: raise # full jitter: wait a random time up to base * 2^n seconds time.sleep(random.uniform(0, base * 2 ** n))Retries without limits are one of the most common causes of self-inflicted outages, which is covered under cascading failures.
Put a limiter at the edge (API gateway or load balancer) that counts requests per key, such as API key, user or IP, and rejects extras with 429 Too Many Requests and a Retry-After header.
Common algorithms:
For many gateway instances, counters need shared state, usually Redis with atomic increments. The trade-off is accuracy versus latency: a central counter is exact but adds a network hop, while per-instance local limits are fast but approximate.
Also decide what happens if the limiter store is down. Failing open keeps the site up but unprotected, and failing closed protects the backend but blocks users. The trap is forgetting that choice.
Scale on a signal that reflects real demand, keep headroom, and remember that the slowest part is usually not the thing being scaled.
The trap is "we have autoscaling" as the whole answer. It reacts after load arrives, so it does not replace capacity planning.
Standardize the pipeline, keep it fast per service, and promote one immutable artifact through environments.
The trap is 200 hand-written pipelines that drift apart, or a central pipeline team that becomes a bottleneck for every change.
A monorepo needs smart build selection. A polyrepo needs shared pipeline standards.
Monorepo: every commit would rebuild everything, so you need path filters or an affected-target build tool, a remote build cache and parallel stages. The reward is atomic cross-service changes and one place to update shared code.
Polyrepo: each repo has its own pipeline, so you need versioned shared templates, automated dependency updates and a service catalogue to keep track. The reward is clear ownership and independent release cycles.
Pick based on how teams work. Tightly coupled services that change together favour a monorepo. Independent teams with separate release cadences favour polyrepo. The trap is choosing a monorepo without investing in build caching, which makes CI time grow with the whole company.
Build the artifact a single time, then promote that exact artifact through every environment instead of rebuilding for each one.
Identify it by an immutable reference, such as the image digest, not a mutable tag like latest. Environment differences (database URL, feature flags, replica count) come from configuration injected at deploy time, not from different builds.
It matters because what you tested must be what you ship. A rebuild for production can pick up a different dependency version or base image, so the binary that reaches users was never tested. It also speeds up releases, since promotion is a metadata change, not a new build.
The trap is a pipeline with separate build jobs per environment, which makes "it worked in staging" meaningless.
Choose by how much risk you can tolerate, how fast you need to roll back, and how much spare capacity you can pay for.
| Strategy | Strength | Cost or risk |
|---|---|---|
| Rolling | Simple, no extra capacity | Mixed versions run together, slower rollback |
| Blue-green | Instant switch and instant rollback | Needs double capacity for a while |
| Canary | Tests on real traffic with small blast radius | Needs good metrics and automation |
Use rolling for low-risk internal services. Use blue-green when a fast, clean rollback matters. Use canary for high-traffic or high-risk services where you can measure error rate and latency automatically.
Database changes cut across all three. While two versions run, the schema must work for both, so it has to be backward compatible. The trap is naming a strategy without saying how you decide it is healthy enough to continue.
Feature flags separate deploying code from releasing a feature. You ship the code switched off, then turn it on for a few users, then for everyone, and turn it off instantly if something goes wrong.
That turns many rollbacks from a redeploy into a flag flip taking seconds. They also allow gradual exposure, internal testing in production and kill switches for risky dependencies.
Rules to keep them healthy: give each flag an owner and an expiry, remove old flags (they become hidden branches in your code), default to the safe value if the flag service is unreachable, and keep the flag service highly available or cached locally. A true rollback is still redeploying the previous image digest. The trap is flags that are never cleaned up and nobody dares to remove.
Measure first: split time into queue wait and run time, because the fixes are different.
To cut run time:
To cut queue time, autoscale ephemeral runners and give teams fair quotas.
For flaky tests, do not just rerun until green, because that hides real bugs and teaches people to ignore red builds. Detect flakes automatically (same commit, different result), quarantine them into a non-blocking job, open a ticket with an owner, and track the flake rate as a metric. The trap is a 40-minute pipeline that engineers learn to route around.
Collect logs at the node, buffer them durably, process them, and store them in tiers.
Design points:
The trap is logging everything at full detail with no retention plan, which makes logging one of the biggest items on the bill.
Collect numeric time series from every service, store them, and alert on them. Pull-based systems like Prometheus scrape endpoints on a schedule, which also tells you when a target is down. Push is better for short-lived jobs that may finish before a scrape, usually through a push gateway or an agent.
Cardinality is the number of distinct label combinations per metric. Every unique set of labels is a separate time series, so a label like user_id or request_id can create millions and exhaust memory and budget.
Rules: label by bounded values (service, route template, status class), never by unbounded IDs; put per-user detail in logs or traces instead. For long retention and many clusters, send data to a long-term store with remote write, and downsample old data. The trap is adding a high-cardinality label "just for debugging" and finding out when the metrics system falls over.
A trace follows one request through many services. Each service creates spans with timing and passes a trace context (for example the W3C traceparent header) to the next call, so the spans can be stitched into one picture.
Tracing everything is expensive, so you sample:
A good policy keeps 100% of errors and high-latency traces plus a small random share of normal ones. OpenTelemetry is the common standard for instrumentation and collection. The trap is turning on 100% tracing in production and being surprised by the bill, or sampling so hard that the one slow request you need is gone.
Store secrets in one hardened, audited system, give workloads short-lived access based on their identity, and rotate often.
Core design:
The trap is committing secrets to Git or baking them into images, and also forgetting that the secret store is itself now critical infrastructure that needs backup and a recovery plan.
Native Kubernetes Secrets are only base64 encoded, so enable encryption at rest in etcd and restrict access with RBAC, and ideally keep the source of truth in an external manager.
Common patterns:
Prefer mounting as files over environment variables, because env vars leak into logs, crash dumps and child processes. Remember that rotation only helps if the application reloads the new value, so decide how it picks up changes. The trap is putting plain secrets in a Git repo "because it is private".
Envelope encryption uses two kinds of keys. A data key encrypts the actual data, and a master key held in a key management service (KMS) encrypts that data key. You store the encrypted data key next to the data.
To read the data, you ask KMS to decrypt the data key, then use it locally. This has three benefits: big data is encrypted locally and fast instead of being sent to KMS, the master key never leaves the KMS, and rotating or revoking the master key controls access to everything under it.
Access is governed by key policies and IAM, and every use is logged. Encryption at rest protects stored data, while TLS protects it in transit, and a design usually needs both. The trap is thinking "the disk is encrypted" means the data is safe from anyone who has permission to read the volume through the cloud API.
Use separate accounts for separate blast radii: production apart from non-production, a security and logging account, and shared services, grouped under an organization with guardrails.
The reward is smaller blast radius, a stronger security boundary and clean cost reporting. The trap is putting every environment in one account separated only by VPCs, where one IAM mistake can reach all of them.
Git holds the desired state of every environment, and a controller inside each cluster continuously pulls it and reconciles the cluster to match.
Why this is good: deployments are pull-based, so CI never holds cluster credentials. Git gives an audit trail and an instant rollback by reverting a commit. Drift is detected and can be self-healed.
Design choices to mention: separate app and config repos, ApplicationSets or app-of-apps to manage many clusters, a controller per cluster (resilient) versus one central hub (simpler), and how secrets are handled without storing them in plain text. The trap is mixing imperative kubectl changes with GitOps, which creates constant drift.
A golden path (or paved road) is the easiest, best-supported way to do a common task, such as creating a new service with CI/CD, monitoring and security already wired in.
It works because it is optional and better. Teams adopt it since it is faster than building their own, not because they are ordered to. A template that creates a repo, pipeline, deploy config, dashboards and alerts in one step is the typical form.
How to do it well: build it with the developers who will use it, keep escape hatches for special cases, enforce only the non-negotiables (security, compliance) as guardrails, and measure adoption. Adoption and time to first deploy tell you whether it is working. The trap is a mandatory gate that developers resent and route around.
Make cost visible to the people who cause it, and put sensible limits in the defaults.
Cost and reliability trade against each other, so present the choice to the business instead of silently picking one. The trap is optimizing the compute line while data transfer and NAT charges quietly dominate.
Start from the access patterns, not from the technology. List the queries, the consistency needs and the expected scale.
Scale relational databases in steps: bigger instance, read replicas, caching, then partitioning or sharding as a last resort. Many real systems use several stores, each matched to one workload. The trap is choosing a NoSQL database because it "scales", then needing joins and transactions that it does not provide.
Combine redundancy for fast failover with backups for recovery from mistakes. They solve different problems.
Replication copies mistakes too: a bad DELETE is replicated instantly, which is why replicas do not replace backups. Also check that the application reconnects cleanly after a failover, because the failover itself can take a minute or two. The trap is a backup job that reports success but has never been restored.
Put a durable queue between whoever creates work and the workers that perform it, so spikes are absorbed and failures are retried.
The trap is no dead-letter queue, so one malformed message blocks or loops forever.
Make it easy to detect, respond, communicate and learn, while keeping the load on people sustainable.
Track metrics such as time to detect, time to mitigate and pages per shift. The trap is an alert for every metric, which creates noise, burnout and ignored pages.
Tier the services by business impact, set RTO and RPO for each tier, and pick the cheapest strategy that meets them.
Include dependencies, since a service that recovers but cannot reach its database, identity provider or secrets store is still down. The trap is a plan that exists only as a document and has never been executed.
Treat the pipeline as production infrastructure, because whoever controls it can ship code to production.
Frameworks like SLSA describe levels of build integrity to aim for. The trap is scanning images but leaving a long-lived deploy key in a CI variable that anyone who can edit the pipeline can read.
Run full capacity in two or more regions at once and route users to the nearest healthy one. The compute is the easy part. The data is the hard part.
The decisions that matter:
Say honestly that this doubles cost and complexity, and that many businesses do not need it. The trap is drawing two boxes and an arrow without discussing data conflicts.
With asynchronous multi-writer replication, two regions can accept different writes to the same record before they sync, so you need a conflict strategy decided in advance.
Also say what the user sees: read-your-writes can break if the next read hits another region, so you may pin a session to a region. The trap is assuming a managed global table solves business conflicts. It resolves the storage conflict, and it will not stop two regions from both selling the last item in stock.
A cascading failure is one where a small problem spreads and grows because the system's own reactions make it worse. The classic loop is overload, then slower responses, then timeouts, then retries, which add even more load.
Retries multiply across layers. If each of 3 layers makes 3 attempts, one user request can send up to 27 calls to the bottom service. Other triggers are exhausted thread or connection pools and a restart that sends everyone to a cold cache at once.
Defences that work together:
Google's SRE book has a chapter on this: Addressing Cascading Failures. The trap is adding retries as the answer to unreliability, when unbounded retries are often the cause of the outage.
A cache stampede happens when many requests all miss the cache for the same key at once, usually because a hot key expired or the cache was restarted, and they all hit the database together.
The database sees a sudden spike for the same query, slows down, and the slowness makes more requests pile up. A cold cache after a deploy or failover creates the same effect for every key.
Ways to prevent it:
The trap is a TTL of exactly 60 seconds on the hottest key in the system, so it expires for everyone at the same instant.
Run each job on a fresh, isolated, autoscaled runner, and make builds fast with layered caching.
Key design choices:
The trap is shared long-lived runners, which are fast at first and become a security and reliability problem.
Use the expand and contract pattern: make the change in small steps, each compatible with both the old and the new code.
-- Step 1: expand, safe and quickALTER TABLE orders ADD COLUMN delivery_city text; -- Build the index without blocking writes (PostgreSQL)CREATE INDEX CONCURRENTLY idx_orders_city ON orders (delivery_city);Every step can be rolled back on its own until the contract step. Watch for operations that lock the table for a long time, since they cause an outage even though no code changed.
The common wrong answer is dropping or renaming a column in the same release as the code change. If you must roll back, the old code now expects a column that is gone.
Alert on how fast you are spending the error budget, not on raw causes like CPU or a single error spike.
An SLO of 99.9% over 30 days gives an error budget of 0.1%. The burn rate is the observed error rate divided by the allowed error rate. A burn rate of 1 uses the whole budget exactly at the end of the window, and 14.4 would use 2% of the budget in one hour.
A common multi-window, multi-burn-rate setup is:
| Burn rate | Windows | Action |
|---|---|---|
| 14.4 | 1 hour and 5 minutes | Page: 2% of budget gone in an hour |
| 6 | 6 hours and 30 minutes | Page: 5% of budget gone in 6 hours |
| 1 | 3 days and 6 hours | Ticket: slow, steady leak |
The short window confirms it is still happening, so you are not paged for something that already ended. This catches fast outages quickly and slow leaks reliably, and it ties every page to real user impact.
The Google SRE Workbook explains the method in Alerting on SLOs. The trap is paging on CPU above 80%, which fires often without any user being affected and trains people to ignore alerts.
Find where the volume comes from, then reduce it with rules that favour the data you actually use.
The usual cost drivers are log volume, metric cardinality and trace volume. Levers for each:
The trade-off is real: every cut risks removing the one clue you need at 3 AM. A good rule is to cut by value, not evenly, and to keep the full detail for a short window plus all errors. The trap is a blanket "reduce logging by 50%" order that deletes exactly the data used in incidents.
Zero trust means no request is trusted because of where it comes from. Every service proves its identity, and every call is authorized.
With mutual TLS (mTLS), both sides present certificates, so the client verifies the server and the server verifies the client. Each workload gets a short-lived identity, for example through SPIFFE and SPIRE or a service mesh's built-in certificate authority, with automatic rotation.
Layers to describe:
Trade-offs: a mesh adds operational complexity, resource use and some latency, and certificate automation must be reliable, because an expired certificate causes an outage. Libraries can do mTLS without a mesh, at the cost of every language needing support. The trap is saying "we have a private network, so it is safe".
Give developers a simple way to get what they need (a service, a database, an environment) while the platform team keeps security, reliability and cost under control behind the scenes.
Design principles:
The trap is building a portal nobody asked for, or a ticket queue with a nicer front end.
Combine several controls, because no single one gives isolation. The standard building blocks are:
| Control | What it limits |
|---|---|
| Namespaces and RBAC | Who can see and change which resources |
| ResourceQuota and LimitRange | CPU, memory and object counts per tenant |
| NetworkPolicy | Which pods can talk to which |
| Pod Security Standards | Privileged containers, host access |
| Dedicated node pools, taints | Noisy neighbours and sensitive workloads |
Be clear about the limits. Namespaces are a soft boundary: tenants still share the kernel, the control plane and cluster-wide resources such as CRDs, admission webhooks and the API server's capacity. That is acceptable for teams inside one company that trust each other, but for hostile or regulated tenants you may need separate clusters or sandboxed runtimes.
Also cover the tenant experience: self-service namespace creation, default quotas, cost reporting per tenant and clear limits so one team cannot exhaust the cluster. Kubernetes documents the options in its multi-tenancy guide. The trap is "one namespace per team, done" with no quotas or network policies.
It depends on blast radius, isolation needs and operational cost. Neither extreme is usually right.
One big cluster: better bin packing and lower cost, one thing to operate, and easy sharing. But a bad upgrade, a broken admission webhook or an API server overload hits everyone, and noisy neighbours are harder to contain.
Many clusters: smaller blast radius, independent upgrades and clear compliance or environment boundaries. But there is more overhead, more idle capacity and a need for fleet management and consistent configuration.
A common middle path is a cluster per environment and region, with production split further by criticality or into cells if the scale demands it, all managed with GitOps so they stay consistent. Choose from your needs: strict isolation, regulated data and independent upgrade schedules push toward more clusters, while a small team with limited time pushes toward fewer. The trap is picking a number first and justifying it afterwards.
Keep the blast radius of every apply small, keep changes reviewed and automated, and make modules the shared building blocks.
The trap is a single repository and state for the entire company, where every apply takes an hour, anyone can break anything and nobody dares to touch it.
Move in small, reversible steps, run old and new side by side, and shift traffic gradually. Data is the hard part.
The trap is a big-bang weekend cutover with no rollback, where the first real test is also the only attempt.
The questions are phrased the way interviewers ask them, based on what recurs in DevOps and platform engineering interview reports. The answers are written from scratch to explain the reasoning behind each design choice, not to be memorized word for word.
It is aimed at DevOps, SRE, platform and cloud engineers preparing for mid-level and senior loops, where design and trade-off questions carry the most weight. Easier questions cover the vocabulary that every design discussion builds on.
No. The ideas are cloud neutral, and a specific service such as Kubernetes, Argo CD or Vault is named only where it makes the example concrete. You can swap in the equivalent service from AWS, Azure or Google Cloud.
Every question is marked easy, medium or hard, and the page shows the split. Easy questions test vocabulary, medium ones test applying a pattern, and hard ones are open scenarios where the right answer depends on stated requirements.