Files
nexus/sreweekly/articles/529/06-how-to-build-your-infrastructure-monitoring-in-2026.html

106 lines
26 KiB
HTML

<!doctype html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>How to Build Your Infrastructure Monitoring in 2026 ·</title><meta name=description content="How to build your infrastructure monitoring in 2026, from SLI/SLO definition to actionable alerting"><link rel=preconnect href=https://fonts.googleapis.com><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=Inter:wght@400;500&family=JetBrains+Mono:wght@400;500&display=swap" rel=stylesheet><link rel=stylesheet href=https://omarghader.github.io/css/style.css><link rel=icon href=https://omarghader.github.io/favicon.ico><script type=application/ld+json>{"@context":"https://schema.org","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"https:\/\/omarghader.github.io\/monitoring-infrastructure-guide-2026\/"},"articleSection":"blog","headline":"How to Build Your Infrastructure Monitoring in 2026","name":"How to Build Your Infrastructure Monitoring in 2026","description":"How to build your infrastructure monitoring in 2026, from SLI\/SLO definition to actionable alerting","inLanguage":"en-US","author":{"@type":"Person","name":"Omar Ghader"},"creator":{"@type":"Person","name":"Omar Ghader"},"publisher":{"@type":"Organization","name":"Omar Ghader","logo":{"@type":"ImageObject","url":"https:\/\/omarghader.github.io\/img/logo.png"}},"accountablePerson":{"@type":"Person","name":"Omar Ghader"},"copyrightHolder":{"@type":"Person","name":"Omar Ghader"},"copyrightYear":"2026","datePublished":"2026-08-05T00:00:00Z","dateModified":"2026-08-05T00:00:00Z","url":"https:\/\/omarghader.github.io\/monitoring-infrastructure-guide-2026\/","wordCount":"1565","image":"https:\/\/omarghader.github.io\/img/logo.png","keywords":["observability","monitoring","sre","opentelemetry","victoriametrics","loki","jaeger","vector",""]}</script></head><body><header class=site-header><div class=wrap><a href=https://omarghader.github.io/ class=site-brand>omar ghader<span>.</span></a><nav class=site-nav><a href=https://omarghader.github.io/work/>Work</a>
<a href=https://omarghader.github.io/blog/>Blog</a>
<a href=https://omarghader.github.io/about/>About</a>
<a href=https://omarghader.github.io/contact/>Contact</a></nav></div></header><main><article class=post-content><img class=post-hero-image src=https://omarghader.github.io/img/it_monitoring_guide_2026.png alt="How to Build Your Infrastructure Monitoring in 2026"><h1>How to Build Your Infrastructure Monitoring in 2026</h1><div class=meta>August 5, 2026
&#183; observability, monitoring, sre, opentelemetry, victoriametrics, loki, jaeger, vector</div><p>Every year I get asked the same question by teams starting from scratch: &ldquo;we have Grafana, we have some dashboards, why do we still get paged for things we didn&rsquo;t see coming?&rdquo; Most of the time, the answer isn&rsquo;t a missing tool. It&rsquo;s a missing method. Teams jump straight to &ldquo;let&rsquo;s install Prometheus&rdquo; or &ldquo;let&rsquo;s buy a SaaS observability platform&rdquo; before answering a much simpler question: what does &ldquo;healthy&rdquo; actually mean for this business?</p><p>I&rsquo;ve built infrastructure monitoring from the ground up for several companies now, and I keep coming back to the same seven steps. This article is that playbook, the way I actually apply it in 2026.</p><h2 id=requirements>Requirements</h2><p>Before you touch any tool, you need:</p><ul><li>A clear list of the critical business flows your system supports (payments, checkouts, logins, API calls…)</li><li>Buy-in from the team on what &ldquo;acceptable&rdquo; looks like for those flows</li><li>A telemetry stack that can handle metrics, logs, and traces (I&rsquo;ll give you mine below)</li></ul><blockquote><p><strong>If you get stuck at any point</strong>: reach out, I&rsquo;m happy to help you think through your specific setup.</p></blockquote><h2 id=1-start-with-the-business-slislo-not-with-the-tool>1. Start with the business SLI/SLO, not with the tool</h2><p>This is the step almost everyone skips, and it&rsquo;s the one that matters the most. Before deciding what to monitor, decide what &ldquo;working&rdquo; means for your business.</p><p>An <strong>SLI (Service Level Indicator)</strong> is a metric that reflects user-facing behavior. An <strong>SLO (Service Level Objective)</strong> is the target you set for that metric over a time window.</p><p>Example, if you work for a banking company:</p><ul><li><strong>SLI</strong>: the ratio of successful payment authorizations over total payment authorization attempts</li><li><strong>SLO</strong>: 99.95% of payment authorizations should succeed over a rolling 30-day window</li></ul><p>That single sentence changes everything downstream. It tells you:</p><ul><li>Which service is &ldquo;tier 0&rdquo; (payment authorization service)</li><li>What your error budget is (0.05% of failed authorizations per month)</li><li>What should page someone at 3am, and what can wait for Monday morning</li></ul><p>Do this exercise for every critical business flow before writing a single scrape config. If you skip it, you&rsquo;ll end up monitoring infrastructure CPU graphs while your actual business metric silently burns through its error budget.</p><h2 id=2-know-what-to-monitor-then-pick-your-stack>2. Know what to monitor, then pick your stack</h2><p>Once your SLIs/SLOs are defined, list what you actually need visibility into to measure them:</p><ul><li><strong>Infrastructure</strong>: nodes, Kubernetes clusters, network, databases, message queues</li><li><strong>Applications</strong>: HTTP servers, HTTP clients, background jobs, gRPC services</li><li><strong>Business layer</strong>: the actual events tied to your SLI (a payment authorization call, a checkout event…)</li></ul><p>Only now do you pick the tech stack, because now you know what it needs to support. Here&rsquo;s the generic stack I use on most projects:</p><table><thead><tr><th>Pillar</th><th>Tool</th><th>Role</th></tr></thead><tbody><tr><td>Metrics</td><td>VictoriaMetrics</td><td>Long-term, cost-efficient metrics storage (Prometheus-compatible)</td></tr><tr><td>Metrics agent</td><td>vmagent</td><td>Scraping and remote-writing metrics</td></tr><tr><td>Logs</td><td>Loki</td><td>Log aggregation, indexed by labels not full text</td></tr><tr><td>Logs & traces ingestion</td><td>OpenTelemetry Collector</td><td>Vendor-neutral receiver/processor/exporter pipeline</td></tr><tr><td>Traces</td><td>Jaeger</td><td>Distributed trace storage and visualization</td></tr></tbody></table><h3 id=the-three-pillars-and-what-each-is-actually-for>The three pillars, and what each is actually for</h3><p>It&rsquo;s worth being explicit about this, because teams often use the wrong pillar to answer the wrong question:</p><ul><li><strong>Metrics</strong>: aggregated, cheap to store, great for trending and alerting. They answer &ldquo;what&rdquo; and &ldquo;how much&rdquo; (error rate is 2%, p99 latency is 800ms).</li><li><strong>Logs</strong>: high cardinality, detailed, expensive to store at full fidelity. They answer &ldquo;why&rdquo; during an investigation (this specific request failed because of X).</li><li><strong>Traces</strong>: the causal chain across services. They answer &ldquo;where&rdquo; in a distributed call the latency or error actually happened.</li></ul><p>None of the three replaces the others. Metrics tell you something is wrong, traces tell you where, logs tell you why.</p><h2 id=3-implement-and-scrape-favor-auto-instrumentation>3. Implement and scrape, favor auto-instrumentation</h2><p>Now you build the pipeline. My rule of thumb: instrument automatically first, add manual instrumentation only where auto-instrumentation doesn&rsquo;t reach (custom business logic, internal queues, batch jobs).</p><p>Use the <a href=https://opentelemetry.io/docs/zero-code/>OpenTelemetry auto-instrumentation libraries</a> for your language. They hook into common frameworks (HTTP servers, HTTP clients, database drivers, gRPC) and emit metrics, traces, and sometimes logs without you writing a single line of instrumentation code.</p><p>Example, a Java service auto-instrumented and shipped straight to your collector, no code change required:</p><div class=highlight><pre style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash>java -javaagent:opentelemetry-javaagent.jar <span style=color:#ae81ff>\
</span><span style=color:#ae81ff></span> -Dotel.service.name<span style=color:#f92672>=</span>payment-authorization-service <span style=color:#ae81ff>\
</span><span style=color:#ae81ff></span> -Dotel.exporter.otlp.endpoint<span style=color:#f92672>=</span>http://otel-collector:4317 <span style=color:#ae81ff>\
</span><span style=color:#ae81ff></span> -Dotel.metrics.exporter<span style=color:#f92672>=</span>otlp <span style=color:#ae81ff>\
</span><span style=color:#ae81ff></span> -Dotel.traces.exporter<span style=color:#f92672>=</span>otlp <span style=color:#ae81ff>\
</span><span style=color:#ae81ff></span> -Dotel.logs.exporter<span style=color:#f92672>=</span>otlp <span style=color:#ae81ff>\
</span><span style=color:#ae81ff></span> -jar payment-service.jar
</code></pre></div><p>On the infrastructure side, vmagent scrapes your Prometheus-format endpoints (node_exporter, kube-state-metrics, cAdvisor, database exporters…):</p><div class=highlight><pre style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=color:#75715e># vmagent scrape config</span>
<span style=color:#f92672>scrape_configs</span>:
- <span style=color:#f92672>job_name</span>: <span style=color:#e6db74>&#39;kubernetes-pods&#39;</span>
<span style=color:#f92672>kubernetes_sd_configs</span>:
- <span style=color:#f92672>role</span>: <span style=color:#ae81ff>pod</span>
<span style=color:#f92672>relabel_configs</span>:
- <span style=color:#f92672>source_labels</span>: [<span style=color:#ae81ff>__meta_kubernetes_pod_annotation_prometheus_io_scrape]</span>
<span style=color:#f92672>action</span>: <span style=color:#ae81ff>keep</span>
<span style=color:#f92672>regex</span>: <span style=color:#66d9ef>true</span>
</code></pre></div><h2 id=4-build-a-telemetry-pipeline-that-correlates-not-just-collects>4. Build a telemetry pipeline that correlates, not just collects</h2><p>Collecting metrics, logs, and traces separately gets you three silos, not observability. The fix is enrichment: attach the same standard labels to everything, so you can pivot from a metric to a trace to a log for the exact same request.</p><p>I always enforce the <a href=https://opentelemetry.io/docs/specs/semconv/>OpenTelemetry semantic conventions</a> resource attributes as a baseline on every service:</p><ul><li><code>service.name</code>: the logical service, e.g. <code>payment-authorization-service</code></li><li><code>service.namespace</code>: the domain/team owning it, e.g. <code>payments</code></li><li><code>deployment.environment</code>: <code>production</code>, <code>staging</code>, etc.</li></ul><p>Enforce this at the OpenTelemetry Collector level so nothing gets ingested without it:</p><div class=highlight><pre style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=color:#75715e># otel-collector-config.yaml</span>
<span style=color:#f92672>processors</span>:
<span style=color:#f92672>resource</span>:
<span style=color:#f92672>attributes</span>:
- <span style=color:#f92672>key</span>: <span style=color:#ae81ff>service.namespace</span>
<span style=color:#f92672>value</span>: <span style=color:#ae81ff>payments</span>
<span style=color:#f92672>action</span>: <span style=color:#ae81ff>insert</span>
- <span style=color:#f92672>key</span>: <span style=color:#ae81ff>deployment.environment</span>
<span style=color:#f92672>value</span>: <span style=color:#ae81ff>production</span>
<span style=color:#f92672>action</span>: <span style=color:#ae81ff>insert</span>
<span style=color:#f92672>service</span>:
<span style=color:#f92672>pipelines</span>:
<span style=color:#f92672>traces</span>:
<span style=color:#f92672>receivers</span>: [<span style=color:#ae81ff>otlp]</span>
<span style=color:#f92672>processors</span>: [<span style=color:#ae81ff>resource, batch]</span>
<span style=color:#f92672>exporters</span>: [<span style=color:#ae81ff>otlp/jaeger]</span>
<span style=color:#f92672>metrics</span>:
<span style=color:#f92672>receivers</span>: [<span style=color:#ae81ff>otlp]</span>
<span style=color:#f92672>processors</span>: [<span style=color:#ae81ff>resource, batch]</span>
<span style=color:#f92672>exporters</span>: [<span style=color:#ae81ff>prometheusremotewrite/victoriametrics]</span>
<span style=color:#f92672>logs</span>:
<span style=color:#f92672>receivers</span>: [<span style=color:#ae81ff>otlp]</span>
<span style=color:#f92672>processors</span>: [<span style=color:#ae81ff>resource, batch]</span>
<span style=color:#f92672>exporters</span>: [<span style=color:#ae81ff>loki]</span>
</code></pre></div><p>With these three labels shared across your metrics, logs, and traces, you can go from &ldquo;error rate spiked on <code>payment-authorization-service</code> in <code>production</code>&rdquo; straight to the matching traces and logs, without guessing.</p><h2 id=5-build-a-red-dashboard-before-anything-fancier>5. Build a RED dashboard, before anything fancier</h2><p>Once telemetry is correlated by <code>service_name</code> and <code>service_namespace</code>, build one dashboard before all others: the <strong>RED dashboard</strong> (Rate, Errors, Duration).</p><p>Apply it to three things per service:</p><ul><li>HTTP server requests (inbound traffic)</li><li>HTTP client requests (outbound calls to dependencies)</li><li>Span metrics (generated from traces, gives you RED per operation, not just per HTTP route)</li></ul><p>Example PromQL for the &ldquo;R&rdquo; and &ldquo;E&rdquo; of a service, using OpenTelemetry&rsquo;s standard <code>http.server.request.duration</code> metric:</p><div class=highlight><pre style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-promql data-lang=promql><span style=color:#75715e># Request rate</span>
<span style=color:#66d9ef>sum</span><span style=color:#f92672>(</span><span style=color:#66d9ef>rate</span><span style=color:#f92672>(</span>http_server_request_duration_seconds_count{service_namespace<span style=color:#f92672>=</span>&#34;<span style=color:#e6db74>payments</span>&#34;}[<span style=color:#e6db74>5m</span>]<span style=color:#f92672>))</span> <span style=color:#66d9ef>by</span> <span style=color:#f92672>(</span>service_name<span style=color:#f92672>)</span>
<span style=color:#75715e># Error rate (%)</span>
<span style=color:#66d9ef>sum</span><span style=color:#f92672>(</span><span style=color:#66d9ef>rate</span><span style=color:#f92672>(</span>http_server_request_duration_seconds_count{service_namespace<span style=color:#f92672>=</span>&#34;<span style=color:#e6db74>payments</span>&#34;, http_response_status_code<span style=color:#f92672>=~</span>&#34;<span style=color:#e6db74>5..</span>&#34;}[<span style=color:#e6db74>5m</span>]<span style=color:#f92672>))</span> <span style=color:#66d9ef>by</span> <span style=color:#f92672>(</span>service_name<span style=color:#f92672>)</span>
<span style=color:#f92672>/</span>
<span style=color:#66d9ef>sum</span><span style=color:#f92672>(</span><span style=color:#66d9ef>rate</span><span style=color:#f92672>(</span>http_server_request_duration_seconds_count{service_namespace<span style=color:#f92672>=</span>&#34;<span style=color:#e6db74>payments</span>&#34;}[<span style=color:#e6db74>5m</span>]<span style=color:#f92672>))</span> <span style=color:#66d9ef>by</span> <span style=color:#f92672>(</span>service_name<span style=color:#f92672>)</span>
<span style=color:#f92672>*</span> <span style=color:#ae81ff>100</span>
<span style=color:#75715e># Duration (p99)</span>
<span style=color:#66d9ef>histogram_quantile</span><span style=color:#f92672>(</span><span style=color:#ae81ff>0.99</span>, <span style=color:#66d9ef>sum</span><span style=color:#f92672>(</span><span style=color:#66d9ef>rate</span><span style=color:#f92672>(</span>http_server_request_duration_seconds_bucket{service_namespace<span style=color:#f92672>=</span>&#34;<span style=color:#e6db74>payments</span>&#34;}[<span style=color:#e6db74>5m</span>]<span style=color:#f92672>))</span> <span style=color:#66d9ef>by</span> <span style=color:#f92672>(</span>service_name, le<span style=color:#f92672>))</span>
</code></pre></div><p>This one dashboard, applied consistently across every service, gives you a clear signal of application health over time before you&rsquo;ve written a single custom panel. Everything else (business dashboards, infra dashboards, deep-dive panels) builds on top of it.</p><h2 id=6-alert-on-symptoms-not-noise-and-use-multi-window-burn-rate>6. Alert on symptoms, not noise, and use multi-window burn rate</h2><p>This is where most on-call setups fail. Two rules I don&rsquo;t compromise on:</p><ul><li><strong>Alert on actionable conditions, not informative ones.</strong> &ldquo;CPU is at 80%&rdquo; is informative. &ldquo;The payment SLO error budget will be exhausted in 2 hours at this burn rate&rdquo; is actionable. If an alert doesn&rsquo;t require a human to do something right now, it shouldn&rsquo;t page anyone; it belongs on a dashboard.</li><li><strong>Use multi-window, multi-burn-rate alerting</strong> so you can tell a critical incident from something that can wait until tomorrow. This is straight out of the <a href=https://sre.google/workbook/alerting-on-slos/>Google SRE book&rsquo;s alerting chapter</a>, and it&rsquo;s the single highest-leverage thing you can implement for on-call sanity.</li></ul><p>The idea: page immediately only when the error budget is burning fast enough that waiting would breach the SLO. Use a short window to catch fast burns and a longer window to confirm it isn&rsquo;t a blip, and use a slower threshold for tickets instead of pages.</p><p>Back to our banking example: SLO is 99.95% success over 30 days, meaning an error budget of 0.05%.</p><div class=highlight><pre style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-yaml data-lang=yaml><span style=color:#75715e># VictoriaMetrics / Prometheus alerting rules</span>
<span style=color:#f92672>groups</span>:
- <span style=color:#f92672>name</span>: <span style=color:#ae81ff>payment-authorization-slo</span>
<span style=color:#f92672>rules</span>:
<span style=color:#75715e># Fast burn: page immediately.</span>
<span style=color:#75715e># Burning 14.4x the allowed rate would exhaust the 30-day budget in ~2 days.</span>
<span style=color:#75715e># Confirmed over both a 5m and 1h window to avoid paging on a blip.</span>
- <span style=color:#f92672>alert</span>: <span style=color:#ae81ff>PaymentAuthSLOFastBurn</span>
<span style=color:#f92672>expr</span>: |<span style=color:#e6db74>
</span><span style=color:#e6db74> (
</span><span style=color:#e6db74> sum(rate(payment_authorization_failed_total{service_namespace=&#34;payments&#34;}[5m]))
</span><span style=color:#e6db74> /
</span><span style=color:#e6db74> sum(rate(payment_authorization_total{service_namespace=&#34;payments&#34;}[5m]))
</span><span style=color:#e6db74> ) &gt; (14.4 * 0.0005)
</span><span style=color:#e6db74> and
</span><span style=color:#e6db74> (
</span><span style=color:#e6db74> sum(rate(payment_authorization_failed_total{service_namespace=&#34;payments&#34;}[1h]))
</span><span style=color:#e6db74> /
</span><span style=color:#e6db74> sum(rate(payment_authorization_total{service_namespace=&#34;payments&#34;}[1h]))
</span><span style=color:#e6db74> ) &gt; (14.4 * 0.0005)</span>
<span style=color:#f92672>labels</span>:
<span style=color:#f92672>severity</span>: <span style=color:#ae81ff>page</span>
<span style=color:#f92672>annotations</span>:
<span style=color:#f92672>summary</span>: <span style=color:#e6db74>&#34;Payment authorization burning error budget fast, will breach SLO in ~2 days if it continues&#34;</span>
<span style=color:#75715e># Slow burn: create a ticket, review during business hours.</span>
<span style=color:#75715e># Burning 3x the allowed rate would exhaust the budget in ~10 days.</span>
- <span style=color:#f92672>alert</span>: <span style=color:#ae81ff>PaymentAuthSLOSlowBurn</span>
<span style=color:#f92672>expr</span>: |<span style=color:#e6db74>
</span><span style=color:#e6db74> (
</span><span style=color:#e6db74> sum(rate(payment_authorization_failed_total{service_namespace=&#34;payments&#34;}[1h]))
</span><span style=color:#e6db74> /
</span><span style=color:#e6db74> sum(rate(payment_authorization_total{service_namespace=&#34;payments&#34;}[1h]))
</span><span style=color:#e6db74> ) &gt; (3 * 0.0005)
</span><span style=color:#e6db74> and
</span><span style=color:#e6db74> (
</span><span style=color:#e6db74> sum(rate(payment_authorization_failed_total{service_namespace=&#34;payments&#34;}[6h]))
</span><span style=color:#e6db74> /
</span><span style=color:#e6db74> sum(rate(payment_authorization_total{service_namespace=&#34;payments&#34;}[6h]))
</span><span style=color:#e6db74> ) &gt; (3 * 0.0005)</span>
<span style=color:#f92672>labels</span>:
<span style=color:#f92672>severity</span>: <span style=color:#ae81ff>ticket</span>
<span style=color:#f92672>annotations</span>:
<span style=color:#f92672>summary</span>: <span style=color:#e6db74>&#34;Payment authorization error budget burning steadily, investigate this week&#34;</span>
</code></pre></div><p>What this buys you on-call:</p><ul><li>A fast, confirmed burn pages someone at 3am, because at that rate you&rsquo;ll breach the monthly SLO within days.</li><li>A slow burn opens a ticket instead of paging, because at that rate you have days to weeks before the budget is exhausted.</li></ul><p>This is the difference between an on-call rotation that burns people out on noise, and one that pages only when it truly matters.</p><h2 id=7-you-now-have-the-method-not-just-the-tools>7. You now have the method, not just the tools</h2><p>At this point you have: business-defined SLIs/SLOs, a stack chosen because it fits what you need to monitor, auto-instrumented metrics/logs/traces, a correlated telemetry pipeline via standard labels, a RED dashboard per service, and burn-rate alerting that tells critical from &ldquo;can wait.&rdquo;</p><p>That&rsquo;s not a finished monitoring setup, it&rsquo;s a solid foundation you can build on: business dashboards, capacity planning, chaos testing against your SLOs, whatever comes next for your organization.</p><h2 id=conclusion>Conclusion</h2><p>If this was helpful, leave a comment and tell me how your monitoring setup looks today. If you&rsquo;d like help implementing any of these steps for your specific stack, I&rsquo;m happy to walk through it with you.</p><p>I wish you calm on-call shifts!</p><section class=cta-block><h2>Building something like this in production?</h2><p>I help teams turn setups like this into reliable, monitored infrastructure.</p><a href=https://omarghader.github.io/contact/ class="btn btn-primary">Get a free consulting call</a></section><div style=margin-top:16px><section style="background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:32px 28px;text-align:center"><p style="font-family:var(--font-display);font-size:19px;font-weight:600;margin:0 0 8px">Get my monitoring stack checklist</p><p style="color:var(--text-secondary);font-size:14px;margin:0 0 20px;max-width:420px;margin-left:auto;margin-right:auto">The exact checklist I use when setting up observability for a new team. No spam, unsubscribe anytime.</p><form class=newsletter-form action=https://omarghader.com/webhook/subscribe/newsletter/omarghader method=post style="display:flex;gap:8px;max-width:380px;margin:0 auto;flex-wrap:wrap"><input type=email name=email required placeholder=you@company.com style="flex:1;min-width:180px;padding:10px 12px;background:var(--surface-2);border:1px solid var(--border);border-radius:6px;color:var(--text);font-family:var(--font-body)">
<input type=hidden value=monitoring name=tag required>
<button type=submit class="btn btn-primary" style=border:none;flex-shrink:0>Send it to me</button></form><p class=newsletter-status style="margin:12px 0 0;font-size:13px"></p></section><script>(function(){document.querySelectorAll('.newsletter-form').forEach(function(a){a.addEventListener('submit',function(e){var b,c,d;e.preventDefault(),b=a.querySelector('button'),c=a.parentElement.querySelector('.newsletter-status'),d=b.textContent,b.disabled=!0,b.textContent='Sending...',c.textContent='',fetch(a.action,{method:'POST',body:new FormData(a),headers:{Accept:'application/json'}}).then(function(b){if(b.ok)a.reset(),c.style.color='var(--accent)',c.textContent="Check your inbox — it's on its way.";else throw new Error}).catch(function(){c.style.color='#e0714f',c.textContent="Something went wrong. Try again in a moment."}).finally(function(){b.disabled=!1,b.textContent=d})})})})()</script></div></article></main><footer class=site-footer><div class=wrap><span>&copy; 2026 omar ghader</span>
<span><a href=https://github.com/omarghader target=_blank>github</a>
&nbsp;&#183;&nbsp;
<a href=https://www.linkedin.com/in/marc-ghader/ target=_blank>linkedin</a></span></div></footer><script>window.addEventListener("load",function(){var b=window._mtm=window._mtm||[],a;b.push({'mtm.startTime':(new Date).getTime(),event:'mtm.Start'}),a=document.createElement('script'),a.async=!0,a.src="https://omarghader.com/matomo/js/container_JgixlzJl.js",document.head.appendChild(a)})</script><script>window.addEventListener("load",function(){var a=document.createElement("script");a.src="https://www.googletagmanager.com/gtag/js?id=G-SMB08YT14H",a.async=!0,document.head.appendChild(a),a.onload=function(){window.dataLayer=window.dataLayer||[];function a(){dataLayer.push(arguments)}a("js",new Date),a("config","G-SMB08YT14H")}})</script></body></html>