CHIRAG Contact Me

CHIRAG

DevOps Engineer|Gamer

Veni. Vidi. Vici.

Scroll

Chapter 01

DEVOPS

Everything below this line is running on this machine right now.

Chapter 01 / Live Infrastructure

One request, end to end.

Running on this machine

01

URL
SHORTENER

Build.Ship.Observe.

A production URL shortener, built and deployed end to end on a single VPS — application, container, reverse proxy, TLS and CI. Not a tutorial follow-along: every layer was a decision, and the interesting part is the decisions.

56.8B

possible short codes

3

API endpoints

17

pinned dependencies

0

records lost per rebuild

The decisions

Each of these had a cheaper option that I chose against. The reasoning is the point.

How are short codes generated?

● secrets random

Six characters from the full alphanumeric set gives about 56.8 billion combinations, which is ample. But size of the space is irrelevant if the generator is predictable. Python’s random is a Mersenne Twister seeded from system time — observe enough outputs and you can reconstruct the state and predict every subsequent code. secrets draws from the OS entropy pool instead. Same code length, same user experience, no enumeration risk.

Why SQLite and not Postgres?

● SQLite Postgres

At this scale Postgres would mean a second process, a second container, a network hop on every query and credentials to manage — real operational weight bought for zero benefit. SQLite is one file with none of that. The important part is that nothing in the codebase is SQLite-specific: everything goes through the ORM, so switching is a connection-string change rather than a rewrite. That is what makes the choice reversible rather than a corner.

Why does the DB dependency yield instead of return?

● yield + finally return

A return hands the session to the handler and the function is done — nothing runs afterwards. If that handler raises, the session is never closed and the connection leaks. Under load that is a slow exhaustion bug that only shows up in production. Yielding inside a try/finally means the framework resumes the generator after the response, and the close runs whether the handler succeeded or blew up.

Why put Nginx in front at all?

● reverse proxy expose the app directly

The application server can speak HTTP perfectly well on its own, so the proxy looks like a spare part until you count what it centralises: TLS termination and renewal, the HTTP-to-HTTPS redirect, static files served without waking the application, and a single public listener instead of one per service. Add a second service later and none of that has to be solved twice.

Why is application data on a volume?

● mounted volume container filesystem

A container’s writable layer dies with the container. Data written there survives exactly until the next rebuild, which turns every deploy into a decision about whether it is worth losing the database. Moving state onto a volume makes rebuilds boring, and boring rebuilds are what let you deploy often enough to keep changes small.

What it does not do yet

Known gaps, with the reasoning for leaving them open rather than the pretence that they are not there.

!

No rate limiting on link creation

Creation is unauthenticated by design, which means anyone can mint links pointing anywhere. That is an abuse vector — someone else’s phishing link on my domain — and it is the first thing I would close. The fix lives in the proxy layer rather than the application, which is exactly why the proxy is there.

!

Schema is created at startup, not migrated

Tables are created from the model definitions when the app boots. That is fine while the schema is stable and I am the only developer, and it stops being fine the moment a column needs to change on live data. Alembic is the answer; I have not added it because there has been nothing to migrate yet, and unused migration infrastructure rots.

!

Horizontal scaling is designed but not deployed

There is a compose definition for multiple replicas behind the proxy, and it is deliberately not running — because pointing several writers at one SQLite file over a shared volume produces lock contention and corruption, not throughput. The database migration has to land before the replica count goes up. Doing them in the wrong order would look like scaling and behave like an outage.

!

Click counts are stored, not surfaced

Every redirect increments a counter and the stats endpoint returns it, but nothing aggregates or graphs it. Real observability means metrics collection and dashboards, which is worth doing when there is enough traffic for a graph to say something.

Still here?

Most people bail at the architecture diagram. You read the part about connection leaks.

Fine. You have earned the other half.

Ok. Enough infrastructure.

LET’S PLAY.

Chapter 02

GAMER

Same operator, different system. This is where the patience comes from.

Cleared

Games I have finished rather than owned. The hard ones are on this list for the same reason the connection-leak bug is on the other one — the interesting part is what it takes to get through.

Ghost of Yōtei Lethal+ Cleared
Sekiro: Shadows Die Twice NG+3 No difficulty setting Cleared
Ghost of Tsushima Complete Cleared
Call of Duty Campaign Cleared

Currently playing

What is actually in the disc tray right now.

In rotation

GTA Online

Long sessions, other people, plans that survive contact with nobody.

In rotation

Far Cry 6

Open world, and a standing refusal to use the map marker.

CHIRAG.EXE

System offline

DevOps is how I build systems.
Gaming is what I do when those systems are not on fire.

Contact GitHub LinkedIn