A shift scheduler on AWS for a few dollars a month
A small team’s rota has more moving parts than it looks. Who’s available which days. Who’s trained to run the till and who isn’t. Who’s already done four shifts this week and shouldn’t do a fifth. The person who asked for next Friday off three weeks ago and the manager who half-remembers saying yes. The Saturday opener who texts at 9pm asking if anyone can cover. Building the weekly rota by hand is a slow, thankless job, and it’s the first thing that goes wrong when the manager is busy. This post walks through the design of a small scheduler that drafts a fair week, asks the manager to approve it, publishes it to staff, and handles swaps without anyone losing track.
Key takeaways
- Three sources for the week: a Drive sheet of staff and shifts, a time-off note lane, and a recurring template lane.
- Every shift ends in one of four outcomes on each draft: filled, fair-swap, short-staffed, or held.
- Fairness by rule: each person has a weekly hours target, and the draft prefers whoever is furthest below it.
- The scheduler only proposes. The manager approves every draft and every swap before anything reaches staff.
- Designed on AWS for about $2.20/month at typical small-team volume.
The whole system on one page
Before any code, here’s the shape of what we’re designing.
What you set up once (the outside)
- Staff and shifts. A Google Sheet in a Drive folder. One tab lists staff: name, role, weekly hours target, the skills they’re cleared for (open, close, till, kitchen, first-aid), a Slack ID or email, and a max-hours cap. Another tab holds each week: the shifts that need covering (day, start, end, role, skills needed) and who’s available. You can fill it in once and reuse it; new things can also enter via two other lanes covered in Part 2 — a time-off note lane (a staffer emails “off next Friday for a wedding” and the scheduler reads it into dates for one-tap approval) and a recurring template lane (the shifts that look the same every week get filled in for you).
- A rules folder. Two short Google Docs in a Drive folder. The rules doc covers the fairness settings — the max-hours cap per person, the minimum rest gap between shifts (so nobody closes at 11pm and opens at 7am), the skill requirements per shift, and how strongly to balance hours against everyone’s target. The voice doc holds the message templates — what the schedule DM and the swap request actually say.
- Manager and staff. The manager approves every draft and every swap. Each staffer has a Slack member ID (so their shifts arrive as a private DM) or, if Slack isn’t set up for them, an email address. Once the manager approves, each person gets only their own shifts — the day, the time, the role — plus a button to request a swap or flag a problem.
What runs on every draft (the inside)
- The week intake. Three sources feed the week. The Drive sheet is the canonical store. New things can also be added via the time-off note lane (a staffer emails
timeoff@your-company.com, the scheduler uses Bedrock Haiku 4.5 to read the plain-English note into a date range, then drops a one-tap approval card in the manager’s Slack to confirm before it’s marked on the week) and the recurring template lane (a small sync Lambda copies the standing weekly pattern into next week’s tab so the manager only edits the exceptions). - The drafter. Runs once a week, by default Thursday at 2pm local, in time for next week. Reads the availability, the hours targets, and the skills. For each shift, it places a qualified, available person, preferring whoever is furthest below their weekly hours target. Picks one of four outcomes. Filled: a clear best person was available and placed. Fair-swap: two people both fit, so the one further below target gets it to even out the hours. Short-staffed: a shift has fewer qualified, available people than it needs — flag it for the manager. Held: a shift the manager marked to assign by hand. The drafter itself doesn’t call a model — the placement logic is plain Python.
- Publish. The full draft goes to the manager first, with an Approve, Edit, or Re-draft button and the fairness summary attached. Nothing goes to staff until the manager taps Approve. On approval, each person gets only their own shifts as a Slack DM (or email), plus a calendar invite per shift. Both honor quiet hours (no pings between 9pm and 7am local by default). Swap requests come back the same way — a qualified replacement is found and the swap is routed to the manager for a quick yes or no. Every send and every approval writes a row in DynamoDB. A weekly fairness summary shows each person’s hours against target.
In plain words
It’s Thursday afternoon. The drafter reads next week: eleven staff, sixty-two shifts to cover, three people who asked for days off, two who can run the kitchen and four who can’t. It places everyone by rule, keeping hours close to each person’s target, and lands a clean draft except for one short-staffed Saturday night that needs a kitchen-trained person who’s already at their cap. That one shift gets flagged. The whole draft — plus the flag — lands in the manager Dana’s Slack at 2:03pm with an Approve button and a one-line note: “Sat night kitchen is short — only Priya is cleared and she’s at 38h.” Dana taps Edit, moves one shift, asks Priya directly about the Saturday, then taps Approve. Each person gets their own shifts and a calendar invite. On Friday, Marco texts that he can’t do Sunday; he taps Request swap, the scheduler finds two cleared people who are under their hours and free, and routes the best one to Dana for a yes. Dana taps yes. Marco’s Sunday is covered, and nobody had to run a group chat.
The cost of running this is about $2.20 a month at small-team volume. The cost of not running it is the Saturday nobody was scheduled to open, the person quietly given every weekend until they quit, and the hour every week the manager spends rebuilding the same grid by hand.
Design rules that shaped every decision
- The scheduler only proposes. The manager approves every schedule and every swap — nothing reaches staff on its own.
- Four outcomes, always. Filled, fair-swap, short-staffed, held. There is no fifth.
- Fairness is a rule, not a vibe. Each person has an hours target and the draft balances against it.
- Quiet hours are respected. A shift ping at 11pm is worse than one at 8am the next day.
- The staff list lives in Drive. Adding a person, changing a skill, or shifting hours doesn’t need a deploy.
- Every draft, approval, and swap is logged. Check why a shift went to someone next month and it’s all there.
Why this shape
Most small teams build the rota one of three ways: a spreadsheet the manager rebuilds every week, a group chat where shifts get claimed and lost, or one person’s memory. The spreadsheet works until the manager is on holiday and nobody else knows the rules. The group chat is chaos with a paper trail nobody reads — two people think they swapped, neither is sure. And memory fails the moment the person who held it isn’t there, which is exactly when the rota matters most.
The setup above keeps the staff list in a sheet the team already edits, but adds a small system that builds the week from it and acts only when something needs a human. The draft comes early enough to fix. It balances hours so the same person isn’t quietly carrying the team. It flags the shifts it can’t fill instead of pretending they’re fine. And it never publishes or confirms a swap on its own — the manager stays in control of who works when. The scheduler is invisible most of the week; it shows up on Thursday afternoon and whenever somebody needs to swap.
The next four posts walk through each piece in turn: how a shift week gets set up, how a fair rota gets drafted, how a schedule reaches the team, and how a shift swap gets approved. One diagram per post. A cost breakdown and a final engineering reference at the end.
All posts