Contents
Course · 14
How do you know it works, keep it up, and keep it safe?
Three jobs every real system has. Seeing what is going on inside (observability). Staying up when parts fail (reliability). Letting in only the right people (security). Every number was checked against official docs, RFCs or the Google SRE books on 2026-10-25.
Measure what users feel, alert on how fast you burn your error budget, plan for whole regions failing, and treat every token and secret as something that will leak.
- Logs say what happened, metrics say how much, traces say where the time went. One trace ID ties all three together.
- Alert on the SLO burn rate, not on CPU. Google's table: page when 2% of a 30-day budget burns in 1 hour (burn rate 14.4).
- RPO = how much data you can lose. RTO = how long you can be down. Cheaper DR → bigger numbers.
- Use OAuth 2.0 authorization code + PKCE for apps; keep JWT access tokens short-lived because they are hard to revoke.
- Hash passwords with Argon2id (min 19 MiB, 2 passes) or bcrypt (cost ≥ 10), per OWASP. The current OWASP Top 10 is the 2025 edition.
code_verifier length, in characters—Words you need
Observability
01The three pillars: logs, metrics, traces
| Pillar | What it is | Question it answers | Cost and limit |
|---|---|---|---|
| Logs | Timestamped event records | "What exactly happened to order 42?" | Huge volume; expensive to keep and search |
| Metrics | Numbers over time, with labels | "Is the error rate up right now? Since when?" | Cheap and fast, but no per-request detail |
| Traces | The path and timing of one request across services | "Which service made checkout slow?" | Usually sampled; needs context passed between services |
Metrics tell you something is wrong; traces tell you where; logs tell you why.
Sources: opentelemetry.io · checked 2026-10-25
02Structured logs and correlation IDs
A plain text log like user 7 paid 20 is easy to read and hard to search. A structured log is one JSON object per line, with named fields. Now you can ask "all errors for order_id=42" in one query.
A correlation ID (or trace ID) is created at the edge and passed to every service in a header. Every log line carries it. One search then shows the whole story of one request.
{"ts":"2026-10-25T10:02:11.482Z","level":"error","service":"payments",
"trace_id":"4bf92f3577b34da6a3ce929d0e0e4736","span_id":"00f067aa0ba902b7",
"order_id":42,"msg":"card declined","provider_code":"do_not_honor"}
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
# version-trace-id (32 hex)-parent span id (16 hex)-flags (01 = sampled)
- Never log passwords, tokens, or full card numbers.
- Use levels (
debug,info,warn,error) and sample noisydebuglogs.
traceparent is a W3C Recommendation (Trace Context Level 1, 23 Nov 2021): version (2 hex), trace-id (32 hex), parent-id (16 hex), trace-flags (2 hex). The OTel SDK's default propagators are tracecontext,baggage (env var OTEL_PROPAGATORS). Source: W3C Trace Context, OTel SDK environment variables.JSON logs + a trace ID in every line turn a log pile into something you can query.
Sources: w3.org/TR/trace-context · OTel SDK config spec
03Metrics: types and what to watch
| Type (Prometheus) | What it does | Example |
|---|---|---|
| Counter | Only goes up (or resets to 0 on restart). You read its rate(). | http_requests_total |
| Gauge | A value that goes up and down | Queue length, memory in use |
| Histogram | Counts values into buckets, plus a sum; percentiles are computed later | Request duration → p99 latency |
| Summary | Computes quantiles in the client over a sliding window | Latency quantiles for one instance (cannot be added across instances) |
Why histograms for latency? Averages hide pain. If 1% of requests take 5 s, the average still looks fine. The p99 (the value 99% of requests beat) shows it.
| Method | Signals | Use it for |
|---|---|---|
| Four golden signals (Google SRE book) | Latency, traffic, errors, saturation | Any user-facing system |
| RED (Tom Wilkie) | Rate, errors, duration | Each request-driven service |
| USE (Brendan Gregg) | Utilization, saturation, errors | Each resource: CPU, disk, network, pool |
Say "RED for services, USE for resources, golden signals for the user view", and use histograms for latency.
Sources: sre.google · prometheus.io · brendangregg.com · grafana.com
04Distributed tracing
Every span has a trace ID (shared by the whole request), its own span ID, a parent span ID, a start time, a duration, and attributes like http.status_code. The tracing UI rebuilds the tree from the parent links.
Sampling keeps cost down. Head sampling decides at the start (say, keep 10%). Tail sampling decides after the request ends, so it can keep all errors and slow requests. Tail sampling needs a collector that buffers whole traces.
traceparent header; across a queue like Kafka you must put the context in message headers yourself (or use an instrumented client). A missing hop splits one request into two unrelated traces. Source: OTel context propagation.Traces answer "where is the time going" across services; propagate context through every hop, including queues.
Sources: OTel traces · OTel context propagation
05SLOs, error budgets and burn-rate alerts
An SLI is a measurement, such as "share of requests that succeed in under 300 ms". An SLO is the target: "99.9% over 30 days". The error budget is what is left: 1 − 0.999 = 0.1% of requests may fail.
In time terms, 0.1% of 30 days is 43.2 minutes (30 × 24 × 60 × 0.001; our arithmetic, not a quote). Budget left → ship features. Budget gone → slow down and fix reliability.
Burn rate = how fast you spend the budget. Burn rate 1 spends it exactly in 30 days. Burn rate 14.4 spends 2% of it in one hour.
Recommended burn-rate alerts for a 30-day SLO
Burn rate that triggers each alert. Hover a bar for its window and action. From the Google SRE Workbook, ch. 5.
Show the full table
| Budget used | Long window | Short window | Burn rate | Action |
|---|---|---|---|---|
| 2% | 1 hour | 5 minutes | 14.4 | Page |
| 5% | 6 hours | 30 minutes | 6 | Page |
| 10% | 3 days | 6 hours | 1 | Ticket |
(error_ratio[1h] > 14.4 * 0.001) and (error_ratio[5m] > 14.4 * 0.001)
# 0.001 = error budget for a 99.9% SLO; the 5m window makes the alert stop fast after recovery
Page on fast budget burn, ticket on slow burn, and never page on CPU alone.
Sources: sre.google/workbook/alerting-on-slos · sre.google/sre-book/embracing-risk
Reliability
06Staying up under stress
503 or 429 fast for some requests, so accepted ones still meet their latency.Why shed load instead of queueing everything? A server past its limit gets slower for every request. Clients time out and retry, which adds more load. Goodput (useful, on-time responses) can fall to almost zero.
Plan for failure: two of everything, fallbacks for extras, fast rejection when full, and drills to prove it.
Sources: AWS Builders' Library · principlesofchaos.org · github.com/Netflix/chaosmonkey
07Disaster recovery: RPO, RTO, backups, regions
RPO (recovery point objective): the most data you accept losing, measured in time. Backups every 24 hours → RPO up to 24 hours. RTO (recovery time objective): the longest you accept being down.
| AWS DR strategy | What runs in the second region | RPO / RTO | Cost |
|---|---|---|---|
| Backup & restore | Only backups; build everything after the disaster | Largest | Lowest |
| Pilot light | Data is replicated; servers are off or minimal | Smaller | Low |
| Warm standby | A smaller, working copy of everything | Smaller still | Medium |
| Multi-site active/active | Full copy serving live traffic | Near zero | Highest |
| Setup | Protects against | Main cost or catch |
|---|---|---|
| Multi-AZ (one region) | One data center failing | Cheap and common; AZs are close, so sync replication is fine |
| Multi-region active-passive | A whole region failing | Async copy → some data loss (RPO above 0); failover must be tested |
| Multi-region active-active | A region failing, with near-zero RTO | Writes in two places → conflicts; needs careful data design |
Backups
- 3-2-1 rule: 3 copies, on 2 different media types, 1 off-site.
- Full + incremental backups, plus point-in-time recovery from database logs.
- Keep at least one copy that attackers cannot delete (separate account, write-once lock).
- A backup you have never restored is a hope, not a backup. Run restore drills.
Ask the business for RPO and RTO first; they pick the DR strategy and its cost.
Sources: AWS DR whitepaper: BCP · DR options · AWS global infrastructure · CISA
Security
08Authentication, authorization, sessions vs tokens
Authentication (AuthN) checks identity: password, passkey, one-time code. Authorization (AuthZ) checks permission: may user 7 delete order 42? Getting AuthZ wrong is the number-one web risk (see section 13).
| Approach | How it works | Good | Bad |
|---|---|---|---|
| Server session | Server stores session data; browser holds a random session ID in a cookie | Easy to revoke (delete the row); small cookie | Needs a shared session store (Redis) to scale |
| Self-contained token (JWT) | Token carries signed claims; any server can check it without a lookup | Stateless, fast across services | Hard to revoke before it expires; bigger; claims can go stale |
Session cookies should be HttpOnly (JavaScript cannot read them), Secure (HTTPS only) and SameSite (limits cross-site sending, which helps against CSRF).
HttpOnly cookie. Service-to-service: short-lived tokens or mTLS. Authorization checks happen on the server for every request, never only in the UI. Source: OWASP Session Management Cheat Sheet.AuthN once, AuthZ on every request, on the server.
Sources: OWASP Session Management Cheat Sheet
09JWT: structure and pitfalls
{ "iss": "https://auth.example.com", "sub": "user_7", "aud": "orders-api",
"exp": 1761390000, "iat": 1761389100, "scope": "orders:read" }
exp. Keep access tokens short (minutes), use refresh tokens you can revoke, or keep a deny-list of jti values.algnone. Watch the RS256 → HS256 confusion attack.exp, iss and aud. A token for another API must be rejected.iss, sub, aud, exp, nbf, iat, jti) are all optional in the spec, so your validator must require the ones you need. RFC 8725 (JWT Best Current Practices) says libraries "MUST enable the caller to specify a supported set of algorithms and MUST NOT use any other algorithms", and describes using an RSA public key as an HMAC secret. Source: RFC 7519, RFC 8725.JWTs trade revocation for speed. Keep them short-lived and validate every claim you rely on.
Sources: RFC 7519 · RFC 8725 · RFC 7009 (token revocation)
10OAuth 2.0 authorization code + PKCE, and OpenID Connect
OAuth 2.0 lets a user give an app limited access to their data on another service, without giving the app their password. The app gets an access token with a scope (what it may do).
PKCE (say "pixy") protects the code. The app makes a random secret, the code_verifier. It sends only a hash of it, the code_challenge, at the start. To swap the code for tokens it must show the original verifier. A thief who steals the code does not have the verifier.
- App makes
code_verifier(random, 43–128 characters) andcode_challenge = BASE64URL(SHA256(verifier)). - App redirects the browser to
/authorize?response_type=code&client_id=…&redirect_uri=…&scope=…&state=…&code_challenge=…&code_challenge_method=S256. - User signs in and agrees.
- Auth server redirects to
redirect_uri?code=…&state=…. App checksstatematches (stops CSRF). - App
POSTs to/tokenwith the code and thecode_verifier. - Auth server hashes the verifier, compares with the challenge, and returns an access token (plus refresh token; with OIDC, an ID token).
- App calls the API with
Authorization: Bearer <token>(RFC 6750).
OpenID Connect (OIDC) adds login on top. Ask for scope openid and you also get an ID token: a signed JWT saying who the user is. The access token is for the API; the ID token is for your app.
redirect_uri matches. RFC 7636 sets the verifier at 43–128 characters and S256 as BASE64URL-ENCODE(SHA256(ASCII(code_verifier))); plain is allowed only if the client technically cannot do S256. RFC 9700 (OAuth 2.0 Security BCP, January 2025) says clients "SHOULD NOT use the implicit grant", public clients MUST use PKCE, and PKCE is RECOMMENDED for confidential clients too. OAuth 2.1 is still an Internet-Draft as of 2026-10-25. OIDC Core requires iss, sub, aud, exp, iat in the ID token. Source: RFC 6749, RFC 7636, RFC 9700, OIDC Core 1.0.For any app, say "authorization code with PKCE (S256), short access tokens, revocable refresh tokens, and OIDC for login".
Sources: RFC 6749 · RFC 7636 · RFC 9700 · RFC 6750 · OAuth 2.1 draft · OIDC Core
11API keys, mTLS, and encryption in transit vs at rest
| Tool | What it proves | Use for | Watch out |
|---|---|---|---|
| API key | "This caller has the key" | Identifying a project or partner, rate limits, simple server-to-server | Long-lived shared secret; no user identity; rotate and scope it; never in mobile or browser code |
| OAuth token | "This user allowed this app this scope" | Acting for a user | Expiry and refresh handling |
| mTLS | Both sides hold a private key for a trusted certificate | Service-to-service in a zero-trust network, service mesh, partner APIs | Certificate issuing and rotation must be automated |
Encryption in transit protects data moving on the network: TLS (HTTPS) everywhere, including inside your VPC. Encryption at rest protects stored data: disks, databases, backups, S3 objects.
At rest, clouds use envelope encryption: a data key encrypts the data, and a master key in a key service (like AWS KMS) encrypts the data key. The master key never leaves the key service, and you can audit and revoke its use.
x5t#S256 claim), so a stolen token is useless without the client's private key. Source: RFC 9846, RFC 8705, AWS KMS cryptography (envelope encryption).Keys identify callers, tokens carry user consent, mTLS proves machine identity; encrypt both on the wire and on disk.
Sources: RFC 9846 · RFC 8446 · RFC 8705 · AWS KMS docs
12Passwords, secrets and least privilege
Never store passwords. Store a slow, salted hash. Fast hashes like SHA-256 are bad here: attackers with GPUs try billions of guesses per second. Password hashes are slow on purpose, and Argon2id and scrypt also need lots of memory, which GPUs lack.
A salt is random bytes stored next to each hash. Two users with the same password get different hashes, so one precomputed table cannot crack everyone. A pepper is an extra secret kept outside the database (optional).
| Algorithm (OWASP order) | Minimum settings (OWASP) | Note |
|---|---|---|
| Argon2id | 19 MiB memory, 2 iterations, 1 thread | First choice |
| scrypt | N = 2^17 (128 MiB), r = 8, p = 1 | If Argon2id is not available |
| bcrypt | Work factor ≥ 10 | Only uses the first 72 bytes of input |
| PBKDF2 | 600,000 iterations, HMAC-SHA-256 | When FIPS-140 compliance is required |
Secrets management
- No secrets in code, Git, images or logs. Load them at runtime from a vault (AWS Secrets Manager, HashiCorp Vault).
- Rotate automatically; prefer short-lived, dynamic credentials (for example IAM roles instead of access keys).
- Least privilege: each service gets only the permissions it needs, on only the resources it needs. Review and trim.
Argon2id with a per-user salt, secrets from a vault, and the smallest permissions that still work.
Sources: OWASP Password Storage Cheat Sheet · NIST SP 800-63B-4 · checked 2026-10-25
13OWASP Top 10, DDoS and CORS
The current list is OWASP Top 10:2025 (the 8th edition). It replaced the 2021 list, so do not quote the old names in an interview.
| # | Risk | One-line example |
|---|---|---|
| A01 | Broken Access Control | Change /orders/42 to /orders/43 and see someone else's order |
| A02 | Security Misconfiguration | Public S3 bucket, debug mode on in production |
| A03 | Software Supply Chain Failures | A hijacked npm package in your build |
| A04 | Cryptographic Failures | Passwords hashed with plain SHA-1, no TLS |
| A05 | Injection | SQL built by string concatenation |
| A06 | Insecure Design | No rate limit on "reset password" |
| A07 | Authentication Failures | No lockout or MFA; sessions never expire |
| A08 | Software or Data Integrity Failures | Auto-update without signature checks |
| A09 | Security Logging and Alerting Failures | A breach nobody notices for months |
| A10 | Mishandling of Exceptional Conditions | An error path that "fails open" and skips the auth check |
DDoS protection basics
A DDoS (distributed denial of service) floods you from many machines. Defend in layers: absorb at the edge (CDN, anycast), filter network floods (layers 3–4), rate-limit and use a WAF (web application firewall) for HTTP floods (layer 7), and auto-scale behind it. Keep origin servers hidden behind the edge.
CORS in one paragraph
Browsers block a page on app.com from reading responses from api.other.com unless that server allows it. CORS (cross-origin resource sharing) is how the server allows it: it answers with Access-Control-Allow-Origin: https://app.com. For "non-simple" requests (for example PUT, or JSON bodies) the browser first sends a preflight OPTIONS request asking what is allowed. The wildcard * is not allowed together with credentials. CORS is enforced only by browsers; it is not an auth system, and curl ignores it.
Know the 2025 Top 10 headline names, put DDoS defence at the edge, and treat CORS as a browser rule, not security for your API.
Sources: OWASP Top 10:2025 · AWS Shield · MDN: CORS · checked 2026-10-25
Practice
—When to use what
| Situation | Pick | Why |
|---|---|---|
| "Is the service healthy right now?" | Metrics + SLO dashboards | Cheap, fast, aggregated |
| "Why did this one request fail?" | Logs filtered by trace ID | Full detail for one event |
| "Which of 12 services is slow?" | Distributed tracing | Shows time per hop |
| Deciding when to page someone | Multiwindow burn-rate alerts | Pages on user pain, not noise |
| Traffic spike past capacity | Load shedding + autoscaling | Keep goodput while scaling up |
| Must survive one data center loss | Multi-AZ | Cheap, sync replication works |
| Must survive region loss, some minutes OK | Pilot light or warm standby | Balance cost and RTO |
| Near-zero downtime, global users | Active-active multi-region | Lowest RTO; accept conflict handling |
| Web or mobile app login | OIDC + authorization code + PKCE | Current best practice (RFC 9700) |
| Need instant logout everywhere | Server sessions, or short JWT + revocable refresh token | JWTs alone cannot be recalled |
| Service-to-service identity | mTLS (service mesh) or short-lived tokens | No shared static secrets |
| Storing user passwords | Argon2id with salt | OWASP first choice |
—Common mistakes
—Interview questions
Show answer
Show answer
traceparent header: a 32-hex trace ID plus the 16-hex parent span ID and flags, which OpenTelemetry propagates by default. Across message queues you must copy it into message headers, or the chain breaks. Return it to the client in a response header too, so support can ask users for it. Follow-up: "What if a third-party service drops the header?" You lose the link for that hop; start a new span linked to the old trace on return.Show answer
http_requests_total, errors_total. You graph its rate(), never the raw value. A gauge goes up and down: queue depth, memory in use, open connections. A histogram counts observations into buckets plus a total sum and count: request duration. From buckets you compute percentiles like p99 on the server side, and you can aggregate across instances, which a Prometheus summary cannot do because it computes quantiles per instance. Choose bucket edges around your SLO threshold (for example 100, 300, 1000 ms) so you can measure "share under 300 ms" exactly. Follow-up: "Why not an average latency gauge?" It hides tail latency and cannot be correctly averaged across instances.Show answer
Show answer
Show answer
Show answer
traceparent, so downstream spans join the same trace. Spans are exported to a collector, then a backend like Jaeger or Tempo rebuilds the tree as a waterfall. Cost control is sampling: head sampling decides at the start (keep 5%), which is cheap but may drop the interesting failures; tail sampling decides after the trace finishes, keeping all errors and slow traces plus a small share of normal ones, but the collector must buffer whole traces. Also limit attribute size and drop noisy internal spans. Follow-up: "How do you link logs to traces?" Put trace and span IDs into every log line.Show answer
Retry-After. Follow-up: "What signal triggers shedding?" Concurrency in flight or queue wait time, not CPU alone.Show answer
Show answer
Show answer
Show answer
Show answer
HttpOnly, Secure, SameSite cookie, and the server looks it up in a store like Redis. They are easy to revoke but need a shared store. Self-contained tokens like JWTs carry signed claims, so any service can verify them without a lookup; that scales well across microservices but makes revocation hard and claims can go stale. Many systems combine them: short-lived JWT access tokens plus a revocable refresh token or server session. Follow-up: "Where do you check authorization?" On the server, for every request and every object.Show answer
alg, kid), payload (claims like sub, exp, aud, iss), and signature over the first two. It is signed, not encrypted, so anyone can read the payload. Pitfalls: (1) revocation: a stolen token is valid until expiry, so keep access tokens to minutes, use revocable refresh tokens, or a deny-list by jti. (2) Algorithm attacks: never trust the header's alg; pin allowed algorithms, reject none, and avoid RS256-to-HS256 confusion, as RFC 8725 requires. (3) All registered claims are optional in RFC 7519, so you must check exp, iss and aud yourself. (4) Storing them in localStorage exposes them to XSS. Follow-up: "How do you rotate signing keys?" Publish keys in a JWKS endpoint and select by kid.Show answer
code_verifier (43–128 characters) and code_challenge = BASE64URL(SHA256(verifier)). It redirects the browser to the authorization endpoint with response_type=code, client_id, redirect_uri, scope, a random state, and the challenge with method S256. The user authenticates and consents. The server redirects back with a short-lived code and the state; the client checks the state. The client then calls the token endpoint directly with the code, redirect URI and verifier. The server hashes the verifier, compares it with the stored challenge, checks the redirect URI, and returns access and refresh tokens. PKCE stops a stolen code from being redeemed. RFC 9700 makes PKCE mandatory for public clients and recommended for all. Follow-up: "Why not the implicit flow?" Tokens in the URL leak; RFC 9700 says clients should not use it.Show answer
openid scope and also receives an ID token, a signed JWT with required claims iss, sub, aud, exp and iat, plus a UserInfo endpoint and discovery metadata. So "Log in with Google" is OIDC; "let this app read my calendar" is OAuth. A common mistake is using an access token as proof of login; it may have been issued to a different client. Follow-up: "What does the client check in the ID token?" Signature, issuer, audience equals its own client ID, expiry and nonce.Show answer
Show answer
Show answer
Show answer
iss, aud, exp with pinned algorithms and per-client rate limits. Services enforce authorization per object (A01 Broken Access Control), use parameterized queries, and validate input. Short access tokens, rotating refresh tokens, revocation endpoint. Secrets in a vault, least-privilege IAM roles, encryption at rest with KMS. CORS allows only our web origins. Audit logs and security alerts feed the SIEM (A09). Dependencies are pinned and scanned (A03 supply chain). Follow-up: "How would you detect credential stuffing?" Login failure rates per IP and per account, plus breached-password checks.—Cheat sheet
| Fact | Value | Source |
|---|---|---|
| Four golden signals | Latency, traffic, errors, saturation | SRE book ch. 6 |
| RED / USE | Rate, errors, duration / utilization, saturation, errors | Grafana / Brendan Gregg |
| 99.9% over 30 days | 43.2 minutes of budget | Arithmetic from SRE definition |
| Burn-rate alerts | 14.4 (1 h) page · 6 (6 h) page · 1 (3 d) ticket | SRE Workbook ch. 5 |
| Short alert window | 1/12 of the long window | SRE Workbook ch. 5 |
traceparent | version-32 hex trace-16 hex span-flags | W3C Trace Context |
| AZ distance | all within 100 km in a Region | AWS global infrastructure |
| Backup rule | 3 copies, 2 media, 1 off-site | CISA paper |
| PKCE verifier | 43–128 characters, method S256 | RFC 7636 |
| OAuth security BCP | RFC 9700 (Jan 2025): no implicit, PKCE everywhere | RFC 9700 |
| ID token required claims | iss, sub, aud, exp, iat | OIDC Core |
| Argon2id minimum | 19 MiB, 2 iterations, 1 thread | OWASP |
| bcrypt | cost ≥ 10, 72-byte input limit | OWASP |
| PBKDF2-HMAC-SHA256 | 600,000 iterations | OWASP |
| TLS 1.3 spec | RFC 9846 (obsoletes RFC 8446) | RFC Editor |
| OWASP Top 10 current | 2025 edition; A01 Broken Access Control | owasp.org |
Reference
—Sources
Course