Making the best decision, not just a working one
Operations Research (OR) is the discipline of using math and models to make better decisions when resources are limited and choices interact. If programming asks "how do I make this run?", OR asks "of all the ways this could run, which one is best — and can I prove it?"
Where it came from
In World War II, Britain put mathematicians and physicists onto military "operations": where to place radar, how big to make convoys, how to hunt submarines. They found that analyzing the operation beat gut instinct — larger convoys lost proportionally fewer ships, for example. After the war those same techniques moved into industry: factories, airlines, supply chains, hospitals. Today it powers ride-share dispatch, delivery routing, power-grid scheduling, and cloud resource allocation — including systems like yours.
Every OR problem is the same three-part sentence: choose these things, to make this as good as possible, without breaking those rules.
— the entire field in one lineThe three ingredients of any model
Decision variables
The knobs you control — what you're actually choosing.
Objective
The single number you want to push as high (or low) as possible.
Constraints
The hard limits the answer must obey.
How an OR project actually flows
Why this matters for you specifically
Georgia Tech's H. Milton Stewart School of Industrial & Systems Engineering (ISyE) is the #1-ranked program of its kind. OR/IE is one of your approved majors — and containershipd is already an OR system in disguise. Every scheduler, quota check, and load balancer you've written is an optimization problem waiting to be named.
Seven tools that cover most of the field
You don't need all of OR to be dangerous. These are the workhorses — each answers a recognizable shape of question. Learn to spot the shape and you'll know which tool to reach for.
Linear Programming (LP)
Split a fixed pool of resources to maximize/minimize a goal, when everything scales linearly.
Integer & Combinatorial Optimization
Same idea, but choices are yes/no or whole units — pick, assign, sequence. Much harder, hence clever algorithms.
Queueing Theory
Predict waiting time and utilization when jobs arrive randomly at limited servers.
Network Flows & Shortest Paths
Route things through a graph of nodes and edges — cheapest path, max throughput, best matching.
Scheduling
Decide what runs when, on which machine, so deadlines are met and idle time is low.
Simulation & Stochastic Models
When the math is too messy, imitate the system with random inputs and measure what happens.
Dynamic Programming
Break a big sequential decision into overlapping sub-decisions and reuse the answers.
Modeling itself
The rarest skill isn't solving — solvers do that. It's translating a messy real system into the three ingredients cleanly. That's the craft ISyE teaches.
Solvers do the heavy lifting
You rarely code the optimization algorithm yourself. You describe the model in a library — OR-Tools, PuLP, Gurobi — and a decades-tuned solver returns a provably optimal (or near-optimal) answer in milliseconds. Your job is the model, not the math grind.
You already use OR every day
These aren't textbook abstractions — they're systems you touch constantly. Each one is a named OR problem under the hood.
Google Maps — the shortest path problem
The road network is a graph: intersections are nodes, roads are edges weighted by travel time. "Fastest route" is literally the classic shortest-path problem, solved by Dijkstra's algorithm (and faster variants) millions of times a second.
The coffee shop line — queueing theory
The diet problem — linear programming's origin story
One of the first LPs (1940s): pick quantities of foods to hit all nutrient minimums at the lowest cost. Foods = decision variables, price = objective, "≥ 2000 calories, ≥ 50g protein" = constraints. The feasible answers form a polygon; the optimum always sits at a corner.
Delivery routing (UPS ORION)
Choosing the order to visit stops — the Traveling Salesman / Vehicle Routing problem. UPS reports ~100M miles saved per year.
Crew & fleet scheduling
Assigning planes and crews to flights under legal rest rules — massive integer programs solved nightly.
Cutting stock
Cut raw sheets/rolls into ordered sizes with the least waste — the same math as packing containers onto a host.
League scheduling
Building a season fixture list that satisfies venue, travel, and TV constraints — a constraint-satisfaction problem.
containershipd is an OR engine
Your daemon manages Docker Compose deployments: it enforces per-user quotas (CPU cores, memory, storage, bandwidth), injects resource limits, polls metrics every 30s, redeploys on webhooks, and routes traffic through Traefik. Almost every one of those is a textbook OR problem.
The deploy flow, seen as a decision pipeline
The core problem: packing deployments onto hosts
Each deployment declares a CPU limit and memory limit. A host has finite cores and RAM. Deciding which deployments fit on which host — using the fewest hosts, or fitting the most valuable set on the host you have — is the bin-packing / knapsack problem. Here's the difference between a naive first-fit and a packed result:
The full mapping
| containershipd feature | OR problem | Method / tool | Payoff |
|---|---|---|---|
| Placing deployments on hosts within CPU/RAM limits | Vector bin packing | ILP / first-fit-decreasing | Fewer servers |
| Admitting the best set of apps onto one host under quota | Multi-dimensional knapsack | ILP / greedy ratio | Max value/host |
| How many concurrent builds/redeploys to run | Queue with c servers | M/M/c, Little's Law | Bounded wait |
| Bursts of webhook redeploys after a big push | Arrival surge / scheduling | Priority scheduling | No thundering herd |
| Safe CPU/memory overcommit ratio | Resource allocation under risk | LP + stochastic/chance constraints | Density w/o OOM |
| Traefik routing subdomain → service:port | Load balancing / assignment | Min-cost flow / round-robin | Even load |
| Metrics polled every 30s, 24h retention | Sampling & forecasting | Time-series / EWMA | Predict, pre-scale |
| Setting default quota tiers (3 deploys, 2 cores…) | Design under a budget | Simulation / LP | Fair & profitable |
The one to build first
Your quota system already tracks each user's maxCpuCores and maxMemoryMb. Turning the "does this deployment fit?" check into a real packing decision — choosing the host that leaves the least wasted capacity — is a self-contained, demoable OR feature. See the next tab for a concrete model.
A packing model you could actually ship
Let's turn one containershipd decision into a real optimization model, end to end: given a host and a set of pending deployments, admit the most valuable set that fits. This is the multi-dimensional (0/1) knapsack.
Step 1 — Name the three ingredients
- Decision variables: for each pending deployment
i, a yes/no variablex_i ∈ {0,1}— admit it to this host or not. - Objective: maximize total value
Σ value_i · x_i(value could be plan tier, priority, or just "count of apps served"). - Constraints: admitted apps can't exceed the host's cores or its memory — two constraints, hence "multi-dimensional".
Step 2 — Write it as math
Step 3 — A tiny instance
Host has 2.0 cores and 2048 MB. Five deployments are waiting. Which set maximizes value?
| Deployment | CPU | Memory | Value | Admit? |
|---|---|---|---|---|
web | 0.5 | 512 MB | 3 | ✓ in |
api | 0.5 | 384 MB | 3 | ✓ in |
db | 0.8 | 768 MB | 4 | ✓ in |
analytics | 1.6 | 1600 MB | 7 | ✗ out — the tempting trap |
cron | 0.2 | 256 MB | 1 | ✓ in |
| Chosen set | 2.0 / 2.0 | 1920 / 2048 | value 11 | optimal |
A greedy "highest value first" heuristic grabs analytics immediately — it's the single most valuable app (value 7) — but it hogs 1.6 cores and 1600 MB, leaving room for only cron. Greedy total: 8. The optimal solver resists the trap, leaves analytics out, and admits the four smaller apps for a total value of 11. That gap — 11 vs 8 — is exactly why OR beats gut instinct.
Step 4 — The solver call (OR-Tools, Python)
Scaling up = the real project
One host is a knapsack. Many hosts is bin packing — add a y_h "is host h used?" variable and a "each deployment goes to exactly one host" constraint, then minimize hosts used. That single extension turns a demo into a genuine cluster scheduler, and it's a great thing to write about in an application essay.
Where to take this next
You have the rare combination the field wants: you can build the system and you can model the decision. Here's how to deepen both.
Concepts to chase
- Linear & integer programming (start with the diet + knapsack problems)
- Queueing basics — Little's Law:
L = λ · W - Graph algorithms: shortest path, max-flow / min-cut
- Greedy vs optimal, and when a heuristic is "good enough"
Tools to try
OR-Tools(Google) — free, batteries-included, great docsPuLP— simplest Python LP/ILP modelingGurobi/HiGHS— industrial-grade solvers (free academic license)SimPy— discrete-event simulation for queue experiments
Three project moves for containershipd
Smart placement
Replace first-fit host selection with the knapsack model from the last tab. Measure hosts saved.
Build queue sizing
Model concurrent builds as an M/M/c queue; pick c so p95 wait stays under a target.
Overcommit dial
Use 24h metrics to safely oversell CPU — a chance-constrained allocation problem.
The GT ISyE angle
If OR/IE is on your list, containershipd is the perfect artifact: a working system where you can point at a real scheduling decision and say "I modeled this as a bin-packing ILP and cut host count by X%." That's exactly the systems-thinking ISyE looks for — engineering plus optimization, not just code.
Operations Research is just refusing to accept "it works" when "it's the best possible" is provable.
— the mindset to carry forwardFrom primer to real code
The last three tabs showed the OR living inside containershipd. This is the plan to actually build it: three optimization problems drawn from your own daemon, on top of one shared toolkit, in a 4–6 week box. The point isn't to prove the optimizer always wins — it's to formulate each problem honestly and report what actually happens.
The honesty rule — this is the whole point
Every problem ships with a benchmark against what the daemon already does (a heuristic, static limits, plain FIFO). If the MILP only ties the greedy heuristic, or loses on runtime, you say so — "the optimum matched first-fit within 2% but took 40× longer, so here's when it's worth it." A reported negative result is a feature of the write-up, not a failure. That posture — I probed my own system and reported the truth — is exactly what an ISyE reader respects.
The shared toolkit: or-lab
Build the plumbing once, and each problem becomes a small module that plugs in. A Python sidecar reads real data out of the Go daemon, hands a formulation to a solver, and grades optimizer vs. baseline.
containershipd needs is a small read-only export command that dumps a snapshot — everything downstream runs on that fixed file, so every benchmark is reproducible.The three problems, side by side
| Spec | OR problem | Method | Real data it uses | Measured against | ~Time |
|---|---|---|---|---|---|
| A · Placement | Multi-host bin-packing | MILP | Container CPU/mem profiles → a simulated fleet | Greedy first-fit | ~2 wk |
| B · Allocation | Resource-limit allocation | LP | Host capacity, quotas, replayed 24h metrics | Current static limits | ~1.5 wk |
| C · Scheduling | Deploy-job scheduling (RCPSP) | MILP | Real deploy log + build durations | FIFO (today's behavior) | ~2 wk |
A — Placement: pack deployments onto the fewest hosts
The knapsack from tab 05, scaled to a fleet: assign each deployment to a host so total CPU and memory fit, using as few hosts as possible. containershipd runs on one host today, so A runs on a simulated fleet built from your real container demands — leaving the door open to a real second machine as a stretch goal.
Honest expected outcome
First-fit-decreasing is often near-optimal, so a tie is the likely — and interesting — result. The hook: "when does the MILP actually save a host, and what does it cost in solve time as the fleet grows?"
B — Allocation: hand out CPU/RAM limits under quotas most code-faithful
Your daemon already injects resource limits and enforces per-user quotas — today with static numbers. Reframe it: given one host's capacity, each user's quota, and each container's real demand, allocate limits to maximize useful work. This runs on a replayed 24-hour metrics trace, so it's the least hand-wavy of the three.
Honest expected outcome
Compare optimized limits vs. today's static ones on the real trace: did it raise useful utilization or improve fairness without OOM-ing anything? Either answer is publishable — and it's grounded in code you already run.
C — Scheduling: sequence the redeploy queue
After a big push, webhook redeploys pile up and their builds fight for CPU. Order and pack the queued jobs to finish sooner (minimize makespan), never exceeding host capacity at any instant. This is the honest version of a scheduling game — run on your real deploy log against the FIFO the daemon does now.
Honest expected outcome
Time-indexed MILPs blow up as the horizon grows, so expect "optimal on short queues, needs a heuristic past N jobs" — a genuine, writeable OR lesson about where exact methods stop paying off.
A rough 5-week shape
- Week 1 — the toolkit. Add the read-only
exportto containershipd; buildor-lab(data model, PuLP/CBC harness, benchmark/report). Ship one tiny end-to-end run so the plumbing is proven. - Weeks 2–3 — Spec B. Most code-faithful, real trace, cleanest baseline. The safest first artifact; if only one gets built, this is it.
- Week 4 — Spec A. Reuse the harness; simulated fleet from real demands. The most instantly-legible "I wrote a MILP" story.
- Week 5 — Spec C + write-up. Scheduling, then the honest blog post: three problems, three verdicts, what held up.
Your call, Sahil — let's decide these together
1. Build order: B first (most defensible, real trace) or A first (most legible as "OR")? 2. The export: OK to add a small read-only export command to the Go daemon, or would you rather or-lab read the existing API? 3. Spec A's fleet: keep it simulated, or is standing up a real second host worth it as a stretch? There's no wrong answer — this tab exists so we can talk it through.
Five questions. Fresh every run.
Each run pulls 5 questions from the bank, rephrases them, and reshuffles the answer options and distractors — so you're recalling the concept, not memorizing a position or wording. Hit Score when you're done, or New run for a different set.
Bank: 11 concepts · 5 shown per run · options & phrasings randomized client-side · no network calls.