Kind-of-Lost Kind-of-Lost · OR Tools · Staff Scheduler · Nurse Schedule · Support

Nurse Scheduling & Ward Rota Generator

A 28-day inpatient ward roster with a real skill mix — solve the loaded sample in one click, then swap in your own unit.

Ward rostering is not really a coverage problem. Coverage is the easy part: you need three RNs on days and two on nights and somebody has to be charge nurse. The hard part is that the same person cannot finish a night at 07:00 and start a day at 07:00, cannot work six nights running, and should not end up with every second weekend while the nurse next to them gets none — and all three of those constraints reach across the midnight boundary where an ordinary weekly scheduling template has nothing to say.

The sample loaded on this page is a 28-day inpatient ward: ten RNs, six aides, two 12-hour shifts, and three separate demand streams — RN headcount, aide headcount, and a charge nurse who must be one of the RNs and must be present concurrently with the RN requirement rather than instead of it. Weekend nights run one RN lighter. Press solve and you get a four-week rota with the rest rule, the consecutive-nights cap and the no-day-after-a-night rule all enforced as hard constraints.

Two things worth knowing before you trust the output. First, the charge-nurse stream carries a much higher shortage penalty than the aide stream, which is how you tell the model that an unstaffed charge shift is a different kind of problem from a missing aide — those numbers are yours to set, and they are what the solver actually trades against labour cost. Second, the results page reports continuity: how many separate blocks of consecutive days each nurse works, how long they are, and how many are lone single days. A rota can be perfectly covered and still be the kind of rota people resign over, and the block numbers are where you see that before your staff do.

Outpatient units — clinics, dialysis, dental, urgent care — usually do not need this page at all. If your demand rises and falls through a working day and nothing runs overnight, the half-hour slot optimizer models that better, because it can invent the shift boundaries instead of taking them as given. Long-term care is a different problem again and has its own page.

Download this sample (.xlsx)
Or drop your own workbook here — choose a file
Same sheet layout as the samples: shifts, demand all, supply all, skills, one grid per demand and per person, parameters. Download a sample first if you want the template.

Nurse rostering questions

Does it handle self-scheduling or shift requests? Partly. A nurse's grid cell can be blank (unavailable), available, or f for a locked-in assignment, so requests that you have already agreed to can be pinned before solving and the model works around them. There is no request-bidding workflow — you decide what gets granted, the tool builds the rest of the rota around it.

Can it enforce a charge nurse on every shift? Yes, and that is what the concurrent flag on a demand is for. A concurrent demand is satisfied by someone who is also counted toward another stream; a non-concurrent one needs a separate body. Charge nurse is the classic concurrent case — the charge nurse is one of your RNs, not an extra.

Is the 11-hour rest rule required in the US? No. Eleven hours is the EU Working Time Directive figure and it is only the default here. There is no federal US equivalent; individual states set their own rules for healthcare and several are shorter. Set MinRestHours to whatever your state law, union contract or unit policy actually says.

Why does it say “feasible” instead of “optimal”? Because the solver ran out of its time budget before it could prove no better roster exists. The roster you got is valid and every hard rule holds — it just is not certified as the cheapest. Raise TimeLimitSeconds if you want the proof.

Can I export the rota to Excel? Yes — a nurse-by-day pivot, the assignment detail, any unfilled shifts, and the continuity numbers, in one workbook.

The same solver, other settings

Each of these loads a different worked example and is written for that setting, but they all run the same cross-day engine — if none of them matches yours, start from whichever is closest and replace the data.

How this differs from the other two modes

The staff scheduling optimizer on the home page works in half-hour slots inside a single day: you say how many people you need at 08:00, at 08:30, at 09:00, and it invents shift blocks to match. That is the right model when demand rises and falls through the day — a call centre, a restaurant, a shop — and the wrong model here, because every one of its rules is scoped to one day column. A 19:00–07:00 night becomes two disconnected half-shifts, and no rest rule can span the midnight boundary.

This page replaces the time axis with a shift axis. Your shifts are named blocks with real clock times, and an end time earlier than the start time simply means the shift crosses midnight. Rest between two shifts then becomes arithmetic on absolute minutes across the whole horizon rather than a special case, which is what makes cross-day rules — minimum rest, maximum consecutive days, maximum consecutive nights, no day shift after a night — expressible at all.

The third mode, the shift rotation generator, does not optimize anything: it lays out a named pattern — DuPont, Panama, Pitman, 4-on-4-off — across four crews. Use it when you want a known, published rotation and you do not need the schedule to respond to who is qualified for what, who is on holiday, or which shifts need three people instead of two.

What the solver actually decides

Coverage, skills, availability, pinned assignments and contracted hours are hard — the model physically cannot assign someone to work they are not qualified for or not available for. Coverage shortfall, minimum-hours shortfall, weekend fairness and cross-person equity are soft: each carries a penalty rather than a prohibition. That is a deliberate choice. A roster tool that answers “infeasible” tells you nothing useful; this one always returns a roster and then names the compromises it had to make, in the warnings and the unfilled-posts table.

Why the continuity numbers are on the results page

A roster can be 100% covered and still be unliveable. Everyone in a pay grade earns the same rate and coverage pins the total hours, so rearranging who works when often costs the model literally nothing — the same ward solved twice with staff listed in a different order produced 21 different shifts at an identical objective. Left alone, the solver returns whichever scattered roster it reached first. So the model carries a small penalty on each new block of consecutive days worked, and another on flipping between days and nights, and the results page reports what came out: how many blocks, how long they are, how many are lone single days, and the tightest turnaround anyone actually got. Those are the numbers to argue with, not the objective value.

Common questions

Is it free? Yes — no account, no trial. Your workbook is read to solve the model and not stored.

How many people and days can it handle? Up to 40 people and 31 days per solve. The 28-day hospital ward sample builds roughly 4,300 variables and solves in well under a minute.

What if I want a shift to be someone specific? Put f in that person's grid cell. Forced cells are hard — the solver schedules around them.

Is minimum rest really 11 hours? Only by default. 11 hours is the EU Working Time Directive figure; the US has no federal equivalent and state rules vary, so it is a parameter (MinRestHours) rather than a constant. Set it to whatever your jurisdiction or contract says.

Get an email when we ship updates to this tool. Low volume, unsubscribe anytime.