[{"content":"Notes on infrastructure, networking, and building systems.\n","date":"22 September 2026","externalUrl":null,"permalink":"/posts/","section":"Blog","summary":"","title":"Blog","type":"posts"},{"content":"","date":"22 September 2026","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","date":"22 September 2026","externalUrl":null,"permalink":"/categories/engineering/","section":"Categories","summary":"","title":"Engineering","type":"categories"},{"content":"","date":"22 September 2026","externalUrl":null,"permalink":"/tags/gpon/","section":"Tags","summary":"","title":"Gpon","type":"tags"},{"content":"","date":"22 September 2026","externalUrl":null,"permalink":"/tags/networking/","section":"Tags","summary":"","title":"Networking","type":"tags"},{"content":"","date":"22 September 2026","externalUrl":null,"permalink":"/tags/opnsense/","section":"Tags","summary":"","title":"Opnsense","type":"tags"},{"content":"I replaced my ISP\u0026rsquo;s ONT/router combo with my own XPON stick. The fiber still comes into my home as usual — but now it lands in a stick in SFU (Single Family Unit) bridge mode that passes the ISP VLAN straight through to OPNsense. No ISP router in the path, no double NAT. OPNsense owns the WAN VLAN interface and the DHCP client.\nThe stick is sold as an HSGQ XPON SFP stick — based on my research, basically an ODI DFP-34X-2C2 underneath, though I am not fully sure, so treat that as best-effort identification. I bought it from a local marketplace (Shopee, affiliate link). It lives in the SFP port of my managed switch, not directly in the router.\nI say XPON for the hardware (it does both) and GPON for the line itself — my ISP runs GPON, and the ONU state below is GPON-specific.\nWhy # Public IP on my router. The ISP ONT does not do bridge mode, and I need the public address on OPNsense — not one NAT behind another box. The stick terminates the PON and bridges frames; one box owns the address and the routing policy. Observability. The ISP box exposes no API and no SNMP. Before, when the WAN went down I could not tell whether it was an ISP routing problem or my fiber line going dark. Now I can query ONU state straight off the stick and watch gateway status in my own monitoring. More on this below. Performance. The ISP unit is a cheap all-in-one — GPON, router, and Wi-Fi in one plastic box. It is out of the forwarding path entirely now. Control. My DHCP, DNS, firewall, and routing policy all live on a box I own — no double NAT, no mystery middlebox deciding what my edge does. Path # The full path looks like this:\nHow it is wired # The stick bridges the ISP VLAN. On the switch SFP port carrying the stick: tagged VLAN 1491 (the VLAN from the ISP), native untagged VLAN 90 (PVID) for stick management — web/SSH lives there. I keep management quarantined on its own VLAN on purpose — the stick firmware is outdated and I do not trust it on any other segment. OPNsense tags VLAN 1491 on its switch uplink and runs DHCP there (WAN VLAN interface). The same uplink gives OPNsense reachability to the stick management address on VLAN 90, which is what the watchdog SSHes to and what the ICMP probes target. The stick exposes ONU state (O1 … O5). O5 means ranging and OMCI are up and the line is operational. Anything below that means the optics are not ready and no amount of DHCP retrying will help. Observability # This is the part I like most. The stick is scraped by Strykar\u0026rsquo;s gpon-exporter straight into Prometheus, and Grafana renders the project\u0026rsquo;s GPON stats dashboard (47 panels, provisioned as-is with my datasource). ONU state, RX/TX power, bias current, SoC temperature, voltage, FEC corrections, BIP errors, alarms — the full optical picture, refreshed every minute.\nThat answers the question the ISP box never could: when the WAN goes down, is it routing (L3) or is the line dark (L1)? RX flatlined with LOS means fiber (L1). Optics fine but gateway down means ISP side (L3). Two different incidents, two different responses, no more guessing.\nMy GPON stats at O5: RX −18.18 dBm, TX 2.13 dBm, all alarms clear — the baseline I compare every outage against. Alongside it, all in the same stack:\nGateway status scraped off OPNsense into Prometheus, with alerts on gateway Offline and a recording rule for which WAN holds the default route. Grafana draws that as a timeline, so failovers show up as events, not mysteries. ICMP probes from OPNsense/Prometheus against the stick management address, so I know whether the stick itself is reachable independent of the ISP path. Watchdog mail from the recovery described below — every outage arrives with duration and repair count, not just \u0026ldquo;it is down\u0026rdquo;. Issues # The pattern that kept biting me: the link goes away and comes back, but the DHCP client never notices.\nFiber cuts are common here. The line drops, the stick re-ranges, and when the light returns OPNsense is still holding a stale lease with no traffic flowing. Stick reboots. Same result: ISP-side lease expires while OPNsense keeps the old one. Remote restarts from the ISP side. Same symptom again: ONT restarts, lease is dead. Either way the manual cure is Interfaces -\u0026gt; Overview -\u0026gt; Refresh on the WAN to pull a fresh lease. My ISP assigns a sticky address via DHCP, so the same address usually comes back. Manual and annoying, and exactly the kind of thing that breaks at the wrong hour.\nAnd the obvious automation is its own trap: a dumb loop reconfiguring the WAN every minute would also fire while the ONT is still ranging — flapping the interface and spamming DHCP with zero chance of success. So there are two problems, not one: the refresh must happen without me, and it must not happen when the line is not ready.\nThe fix: only refresh on O5 # Both problems have the same answer: automate the Refresh I did by hand, but gate it on the optics. Notice the path is bad, confirm the line is really back (O5), then pull a fresh lease. It runs on OPNsense itself, no external dependency, every minute:\nRead the WAN IPv4 on the VLAN interface. Probe an external target (ISP gateway / reliable public IP) bound to that source address. If the probe succeeds, the path is fine. Track the stick management IP for a down-to-up rising edge (catches reboots). Ask the stick for ONU state over SSH (diag gpon get onu-state). Rules:\nIf GPON is not O5: do not reconfigure. Queue one notification until the state changes. Reconfiguring a non-ready ONT only flaps. If GPON is O5 and probes fail for 2 consecutive minutes: reconfigure the WAN interface (configctl interface reconfigure). Keep retrying about once a minute while it stays O5 and bad. A stick reboot edge plus bad path repairs immediately, still gated on O5. State is small files under /var/db/ (fail count, GPON state, stick edge, alert flags) plus one log file. Cooldown is disabled on purpose — the O5 gate is the guardrail, and in my experience the ISP DHCP server has tolerated rapid client retries.\nOne stick quirk: its SSH needs legacy key exchange (diffie-hellman-group1-sha1 / ssh-rsa), and the password is fed via SSH_ASKPASS so it never lands in argv or logs. Credentials live on the box only, never in git.\nMail without spam # OPNsense sends mail to me over Gmail SMTP. I split it:\nGeneric async outbox: any service drops a *.mail file, a worker sends it once a minute. The watchdog never talks SMTP directly. The watchdog enqueues on failure (first 2 mails immediate, then at most one combined mail every ~30 minutes while still down, counting suppressed attempts), on recovery with outage duration and repair count, and one-shot when GPON is not ready. Monit watches the check in failure-only mode and restarts core processes (DNS, time sync, SSH, tunnel daemon). Monit success mail was noise. Failure-only plus explicit recovery mail was the right split.\nWhat I kept # No repair without O5. That single check removed all the flap-reconfigure spam after reboots. Stick port with tagged 1491 and untagged 90 for management, quarantined on its own VLAN — the firmware is outdated and untrusted, so nothing else sits on that segment. OPNsense owns DHCP and policy. The stick stays dumb: terminate PON, bridge frames. Everything reversible from the UI: Cron toggle, Monit toggle, no hidden state. If you run an XPON stick on a GPON line into OPNsense, the lessons travel well: keep the stick in bridge and let the router own the VLAN + DHCP, make the line observable separately from routing, and gate any DHCP repair on optical state.\n","date":"22 September 2026","externalUrl":null,"permalink":"/posts/xpon-stick-sfu-opnsense/","section":"Blog","summary":"An XPON stick in bridge mode passing the ISP VLAN straight to OPNsense, plus a watchdog that only repairs DHCP when the optics are really ready.","title":"Replacing the ISP ONT with my own XPON stick in SFU mode","type":"posts"},{"content":"I\u0026rsquo;m a hands-on engineer and tech lead. I design backend services and platforms to be reliable and secure.\nOpen to senior roles in backend, SRE/infrastructure, and technical leadership.\nRemote · global · full-time or contract.\nEmail · LinkedIn · Resume\n","date":"22 September 2026","externalUrl":null,"permalink":"/","section":"Reyn Hartono","summary":"","title":"Reyn Hartono","type":"page"},{"content":"","date":"22 September 2026","externalUrl":null,"permalink":"/tags/sre/","section":"Tags","summary":"","title":"Sre","type":"tags"},{"content":"","date":"22 September 2026","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"22 September 2026","externalUrl":null,"permalink":"/tags/xpon/","section":"Tags","summary":"","title":"Xpon","type":"tags"},{"content":"I\u0026rsquo;m releasing CFEG (Cloudflare Email Gateway): open tooling on Cloudflare Email Routing so you can keep what hits your domain when you want, forward on your terms, and send from the address the sender wrote to — including catch-all privacy locals — without drowning in Gmail Send mail as setup.\nCode (MIT):\nGateway: github.com/reynhartono/cf-email-gateway Gmail Reply extension: github.com/reynhartono/cfeg-reply-extension This post is the short story of why it exists and what it does.\nWhy I built it # I run catch-all privacy mail on domains I control. Each service gets its own local-part — netflix@…, github@…, that-one-marketplace@… — so a leak or a spam wave points at one alias, not my whole life.\nThat only works if two things stay true:\nMail that hits the domain is still mine if the inbox behind the forward is having a bad day. I can answer as the address the sender wrote to, not as a single shared \u0026ldquo;real\u0026rdquo; mailbox. Cloudflare Email Routing made the first half easy: MX, catch-all, forward into Gmail. The second half looked free until it wasn\u0026rsquo;t.\nForwarding is not a vault. Destinations go down. Mailboxes fill up. Rate limits bite. If the only copy was \u0026ldquo;whatever made it through,\u0026rdquo; a failed forward is a silent loss. I wanted a durable keep of what arrived at my domain when I chose to archive — independent of Gmail\u0026rsquo;s mood that minute.\nGmail Send mail as does not scale. It is fine for a handful of From addresses. It is not a plan for hundreds of privacy locals. I was not going to add and verify every shop and SaaS alias under Accounts → Send mail as. So the easy path is to reply from the catch-all inbox address, break the sender\u0026rsquo;s expectation of who they wrote to, or abandon unique locals the first time a real thread matters.\nI did not set out to ship an email product. I set out to stop losing mail and stop hitting Reply from the wrong From address. CFEG is what I built for that — so others on the same path can run it themselves.\nWhat it does # Capability What you get Archive on receive (optional) Durable copy of inbound MIME when you enable it; survive dest outages, full storage, rate limits Forward (optional) Still land mail in your inbox — or skip forward for keep-only / quiet sink Reply tokens / hop Send From the original recipient address without per-alias Send mail as Compose as any address New mail from catch-all / role identities in the same model CFEG Reply (Gmail) Reply / Reply-All in the UI you already use, routed through the hop Archive and forward are independent. Run archive + forward, archive only, or forward only — your routing config, your call.\nHow it fits together # Inbound — your policy, not a fixed pipeline # Sender │ ▼ Cloudflare Email Routing │ ▼ CFEG Worker │ ├─► Archive (optional) │ └─► Forward (optional) ──► Your inbox When archive is on, the kept copy does not depend on the destination being up, having free storage, or accepting more mail right then.\nOutbound — send From the address they wrote to # Gmail (CFEG Reply) or send/reply client │ ▼ CFEG hop (reply / compose token) │ ▼ Sent with the real From (catch-all local-part, role address, … — no per-alias Gmail \u0026#34;Send mail as\u0026#34;) I still live in Gmail day to day. The extension makes the hop usable without adding every alias under Send mail as.\nWho it\u0026rsquo;s for # Domains already on Cloudflare Email Routing that need more than best-effort forward Catch-all privacy setups where unique local-parts are the product, not an edge case Anyone juggling many identities on one domain and tired of Gmail Send mail as as the only way to pick From If you only need one alias and never care about a receive log, plain Email Routing is enough. CFEG is for when the domain is the identity surface.\nTry it # Clone and read the install guide in the gateway repo: docs/18-installation.md Deploy the Worker, point Email Routing catch-all at it, set your routing YAML and secrets Optional: install CFEG Reply for Gmail Reply / Reply-All You own the stack — Cloudflare account, R2/D1, SMTP, policy. CFEG is tooling around Email Routing, not a hosted mailbox.\nClosing # I built CFEG so mail that hits my domains can be kept when I choose, and every address those domains own can appear in From — not only the few I bothered to add under Gmail Send mail as.\nIf that matches how you run mail, the code is here:\ncf-email-gateway cfeg-reply-extension Issues and PRs welcome. Wire it to your own routes, storage, and policy.\n","date":"12 September 2026","externalUrl":null,"permalink":"/posts/cf-email-gateway/","section":"Blog","summary":"CFEG — a Cloudflare Email Routing gateway with optional archive and forward, plus send-from any catch-all address. Why I built it, and how to try it.","title":"CF Email Gateway - A Worker between Email Routing and my inbox","type":"posts"},{"content":"CFEG is a Worker gateway on Cloudflare Email Routing: keep what hits your domain when you want, forward on your terms, and send From the address the sender wrote to — including catch-all privacy locals — without adding every alias under Gmail Send mail as.\nProblem # Forwarding is lossy — destination down, mailbox full, rate limits → mail can vanish if the forward was the only copy Many identities — catch-all privacy (netflix@…, github@…, …) does not fit Gmail Send mail as at scale What it does # Capability Notes Archive (optional) Durable inbound keep when enabled Forward (optional) Independent of archive — both, either, or policy-driven Reply / compose hop Real From via tokens — not per-alias Send mail as CFEG Reply Gmail extension for Reply / Reply-All through the hop Links # Code: cf-email-gateway · cfeg-reply-extension Story: CF Email Gateway - A Worker between Email Routing and my inbox Open tooling you run on your own Cloudflare account, routes, and storage.\n","date":"12 September 2026","externalUrl":null,"permalink":"/work/cf-email-gateway/","section":"Work","summary":"Optional archive and forward on Cloudflare Email Routing. Send From any catch-all address without Gmail Send mail as setup per alias.","title":"CFEG — Cloudflare Email Gateway","type":"work"},{"content":"","date":"12 September 2026","externalUrl":null,"permalink":"/tags/cloudflare/","section":"Tags","summary":"","title":"Cloudflare","type":"tags"},{"content":"","date":"12 September 2026","externalUrl":null,"permalink":"/tags/email/","section":"Tags","summary":"","title":"Email","type":"tags"},{"content":"","date":"12 September 2026","externalUrl":null,"permalink":"/tags/gmail/","section":"Tags","summary":"","title":"Gmail","type":"tags"},{"content":"","date":"12 September 2026","externalUrl":null,"permalink":"/tags/privacy/","section":"Tags","summary":"","title":"Privacy","type":"tags"},{"content":"","date":"12 September 2026","externalUrl":null,"permalink":"/categories/projects/","section":"Categories","summary":"","title":"Projects","type":"categories"},{"content":"Selected projects and public tools. Longer stories live under Blog when there is one.\nProjects # CFEG — Cloudflare Email Gateway — optional archive/forward on Cloudflare Email Routing; send From catch-all addresses without per-alias Gmail Send mail as Retail POS for furniture — Laravel POS: sales, stock allocation, deliveries, payments, accounting reports, granular RBAC ","date":"12 September 2026","externalUrl":null,"permalink":"/work/","section":"Work","summary":"","title":"Work","type":"work"},{"content":"","date":"17 September 2022","externalUrl":null,"permalink":"/tags/bash/","section":"Tags","summary":"","title":"Bash","type":"tags"},{"content":"I first needed this while running GNU ddrescue on a friend\u0026rsquo;s dying hard drive. Bad reads often show up as null (0x00) padding in the image. Counting non-null bytes was a rough \u0026ldquo;how much did we salvage?\u0026rdquo; signal — and, stacked against their memory of how full the disk had been, a ballpark % recovered.\nSame idea shows up elsewhere: sparse dumps, padded blobs, firmware images, or any file where \u0026ldquo;how much real payload is in here?\u0026rdquo; matters more than stat size.\nSimple count # Strip nulls with sed, then count bytes with wc -c:\nsed \u0026#39;s/\\x00//g\u0026#39; /path/to/file | wc -c That is the whole trick: delete \\x00, measure what remains. On a rescue image, treat the result as salvaged payload bytes (not a forensic guarantee — legitimate data can contain nulls too — but good enough for a recovery estimate).\nLarge files need progress # Rescue images are huge. The pipeline can run a long time with no feedback. Put pv in front so you see throughput and ETA:\npv /path/to/file | sed \u0026#39;s/\\x00//g\u0026#39; | wc -c pv reads the file, shows how much has been consumed, and feeds sed the same stream.\nTwo meters: input vs non-null output # If you want bytes read and non-null (salvaged) bytes so far side by side, use two named pv stages (-N) and keep the counters on screen (-c):\npv -N in -c /path/to/file | sed \u0026#39;s/\\x00//g\u0026#39; | pv -N out -c | wc -c Meter Meaning in Progress through the image / original file out Non-null bytes emitted after sed wc -c Final non-null total when the pipe finishes Rough recovery % (if you trust a \u0026ldquo;disk was about F% full\u0026rdquo; memory):\nrecovered ≈ (non_null_bytes / image_size) / (F / 100) Or simpler: compare non_null_bytes to F% × original_disk_size when you know the drive capacity.\nNotes # Needs a sed that understands \\x00 in the replacement pattern (GNU sed is the usual case on Linux). This streams; it does not load the whole file into a single string. Still, sed byte-rewriting is not the fastest tool for multi‑GB rescue images — fine for ops one-offs, not a benchmark harness. For a quicker strip on many systems, tr -d '\\000' can replace sed in the same pipeline shape if you only care about deleting nulls. Nulls ≠ only failed reads; use this as an estimate, not proof of file-level recovery. ","date":"17 September 2022","externalUrl":null,"permalink":"/posts/counting-non-null-bytes-of-a-file/","section":"Blog","summary":"Count non-null bytes after a GNU ddrescue image (and other blobs) with sed, wc, and pv progress.","title":"Counting non-null bytes of a file","type":"posts"},{"content":"","date":"17 September 2022","externalUrl":null,"permalink":"/tags/linux/","section":"Tags","summary":"","title":"Linux","type":"tags"},{"content":"","date":"21 July 2021","externalUrl":null,"permalink":"/tags/accounting/","section":"Tags","summary":"","title":"Accounting","type":"tags"},{"content":"","date":"21 July 2021","externalUrl":null,"permalink":"/tags/inventory/","section":"Tags","summary":"","title":"Inventory","type":"tags"},{"content":"","date":"21 July 2021","externalUrl":null,"permalink":"/tags/laravel/","section":"Tags","summary":"","title":"Laravel","type":"tags"},{"content":"","date":"21 July 2021","externalUrl":null,"permalink":"/tags/php/","section":"Tags","summary":"","title":"Php","type":"tags"},{"content":"","date":"21 July 2021","externalUrl":null,"permalink":"/tags/pos/","section":"Tags","summary":"","title":"Pos","type":"tags"},{"content":"","date":"21 July 2021","externalUrl":null,"permalink":"/tags/rbac/","section":"Tags","summary":"","title":"Rbac","type":"tags"},{"content":"Custom point of sale, inventory, and accounting for furniture retail — order entry through stock allocation, delivery, payments, and financial reports, with role-based access control.\nProblem # Furniture retail runs on multi-line orders, partial stock moves, deliveries, and open balances. Spreadsheets and disconnected notes make it hard to keep sales, warehouse, and books aligned — and to limit who can finalize a sale or touch the ledger.\nWhat it does # Area Capabilities Masters Products, suppliers, customers, staff, users Purchases Supplier buys; create / update / finalize; purchase payments Sales Orders and sales flow; line items, notes, invoice no., salesperson, status Stock allocation Per-line allocate ordered qty before ship Deliveries Create and view shipment / delivery records Payments Sale and purchase payments; remaining balance on the order Inventory Stock tied to transactions; inventory stock summary report Accounting Chart of accounts + journal/transactions Financial reports Trial balance, general journal, general ledger, income statement, balance sheet, equity change Sales reports Orders and sales transactions Dashboard KPI cards (purchases, sales, shipped, revenue) + income chart Access control RBAC with granular permissions (view / create / update / delete / finalize per domain) UI is Indonesian-first (e.g. Penjualan, Alokasi Stok, Tambah Pembayaran) on an AdminLTE + Bootstrap admin shell.\nScreenshots # Dashboard # KPI strip and revenue chart (Pendapatan) with range filters.\nSales order # Order header, line items, totals and remaining balance, print, add payment, and Alokasi Stok rows (e.g. still 0.00/1.00 unallocated).\nStack # Backend: PHP / Laravel 8.41.0 UI: Bootstrap + AdminLTE AuthZ: RBAC — granular permissions per resource and action (masters, sales/purchases including finalize, allocations, deliveries, payments, accounting, reports, dashboard, users) Data: MySQL (shared-hosting style deploy) Locale: IDR formatting, Indonesian labels Role # Sole builder — design through deploy for day-to-day store operations.\nLinks # Code: private / closed source ","date":"21 July 2021","externalUrl":null,"permalink":"/work/pos-inventory-accounting/","section":"Work","summary":"Laravel POS for furniture retail — sales, purchases, stock allocation, deliveries, payments, and accounting reports with granular RBAC.","title":"Retail POS for furniture","type":"work"},{"content":" Now # I\u0026rsquo;m Reyn Hartono. I work as a hands-on engineer and tech lead: backend, SRE/infrastructure, and security.\nMost recently I was Engineering Team Lead at The Software Practice (Singapore, remote), close to the code, delivery, and production quality. Before that I was a Site Reliability Engineer, then Senior SRE, at Verihubs. I helped stand up SRE practice, multi-cloud infrastructure, GitOps, and observability.\nI\u0026rsquo;m open to senior backend and SRE/infrastructure roles, and to technical leadership where I stay hands-on.\nRemote · global · full-time or contract.\nHow I work # I care about reliability and security while the system is still being designed. Failure should be explainable, recovery should be something we can run, and authorization should stay tight. Services should remain operable under load. Automation should make the safe path the usual one.\nProof (sanitized) # Raised a production service path from ~100 rps to ~3,000 rps without scaling the infra out. Microservices behind KrakenD (Go), Python FastAPI services (auth + domain), SQS-backed async work, PostgreSQL. Designed and implemented Stripe callback and payment tracking so provider events and internal state stay consistent. Early SRE hire at Verihubs: IaC, Kubernetes, GitOps (ArgoCD), multi-cloud networking, centralized observability. Reported vulnerabilities to multiple companies, including Bukalapak (others asked not to be named). How I lead # As a tech lead I stay hands-on: shipping, reliability, and incidents. I work with product, PM, and QA as needed.\nStack # Area Strong / recent Backend C# / .NET Core, PHP / Laravel; also Java, Node.js. Open to Go and deeper Java Cloud GCP, AWS Infra Terraform/OpenTofu, Ansible, Docker, Kubernetes, Helm, bare metal where it matters CI/CD GitLab CI, ArgoCD Observability Grafana, Loki, Tempo, VictoriaMetrics Data PostgreSQL, MySQL, Redis, SQLite Network TCP/IP, VPNs, API gateways (incl. KrakenD), packet analysis, MikroTik, FortiGate Edge / email Cloudflare (see CFEG) Background # I started programming young on a family PC with a single Visual Basic 6 book. That habit never really left: understand the system, then harden it.\nContact # me@reyn.id · LinkedIn · GitHub · Resume\n","externalUrl":null,"permalink":"/about/","section":"Reyn Hartono","summary":"Tech lead. Backend, SRE/infra, security","title":"About","type":"page"},{"content":"","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":" Download # Download resume (PDF)\nOr reach me by email or LinkedIn.\nAt a glance # Focus: backend · SRE/infrastructure · security · technical leadership Remote · global · full-time or contract Experience (summary) # Engineering Team Lead — The Software Practice Pte Ltd (Singapore, remote) # Dec 2023 – Feb 2026 Tech lead. Backend services and client systems in C# / .NET Core. APIs and business logic on delivery timelines. Validation layers to cut bad input and production error noise. Worked with PM and QA on defects and production readiness.\nSenior Site Reliability Engineer — Verihubs (Jakarta, remote) # Feb 2022 – Nov 2023 Multi-cloud GCP/AWS architecture. IaC for repeatable change. GitLab CI + ArgoCD. Observability with Grafana / Loki / Tempo / VictoriaMetrics. Multi-cloud network design (VPN, cross-cloud, vendor access, isolation). API gateway tuning. Mentored interns and new engineers.\nSite Reliability Engineer — Verihubs (Jakarta, remote) # Jun 2021 – Jan 2022 Built foundational SRE practice: Kubernetes, Terraform, ArgoCD/GitOps. PostgreSQL operations. Self-hosted GitLab. Container workload reliability. Performance work with product engineers. Supported ISO 27001 security and compliance efforts.\nNetwork Administrator \u0026amp; Technical Support — BINUS University # Sep 2019 – Aug 2021 Automation for lab and infrastructure operations. Linux/Windows servers. Campus network (routing, switching, firewalls, access control). Wireshark-level diagnosis. Active Directory. Datacenter hardware work.\nTeaching Assistant — BINUS University # Sep 2018 – Aug 2019 Undergraduate practicums covering programming, penetration testing, secure web development, forensics, operating systems, and networking.\nFreelance Web Developer # Jun 2011 – Jul 2018 Full-stack work via Fiverr and Upwork (PHP/Laravel, WordPress, hosting migrations, Linux, legacy fixes). High client volume with strong ratings.\nSkills # Backend: PHP, Laravel, C#, .NET Core, Java, Node.js Cloud: GCP, AWS Infra: Terraform, Ansible, Docker, Kubernetes, Helm CI/CD: GitLab CI, ArgoCD Observability: Grafana, Loki, Tempo, VictoriaMetrics Data: MySQL, PostgreSQL, SQLite, Redis Networking: TCP/IP, VPNs, API gateways, packet analysis, MikroTik, FortiGate\n","externalUrl":null,"permalink":"/resume/","section":"Reyn Hartono","summary":"CV — Reyn Hartono","title":"Resume","type":"page"},{"content":"","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"}]