← all spins
SPIN 03LOGISTICS · SCHEDULING · DATA
2026-06-22 · 11 min read

Drayage appointments nobody can keep

Rebuilding a container terminal appointment system as a scheduling problem, using published gate-turn-time data. The published slots were infeasible a third of the time.

-31%
INFEASIBLE SLOTS AVOIDED
GitHub Repo↗

01 The problem

Ports and rail ramps allocate 1-hour appointment windows for container drayage trucks. On paper, the capacity matches crane throughput. In reality, drivers arrive to find queues stretching onto access highways, turn-times tripling, and appointments forfeited through zero fault of the carrier.

The system was optimizing for terminal gate smoothing rather than round-trip feasibility for the motor carrier.


02 The data

Public terminal throughput tables and gate logs from West Coast port data feeds.

SOURCE RANGE ROWS
Gate turn-time logs 2024-01 – 2026-05 124,800
Hourly vessel discharge logs 2024-01 – 2026-05 21,200
Chassis inspection records 2025-01 – 2026-04 14,500

03 What I built

A bipartite matching algorithm that couples terminal appointment slot availability with motor carrier round-trip feasibility windows. Instead of a static first-come-first-served gate reservation, appointments are offered as dynamic bundles that guarantee return-bobtail viability.


04 Result

SCHEDULER MEAN TURN TIME INFEASIBLE SLOTS COMPLIANCE
Static FIFO Portal 84 min 34.2% 61%
Round-Trip Feasible Matching 51 min 3.1% 94%

05 Where it broke

  1. Unannounced chassis flips. Drivers reserved dry vans but were reassigned reefer chassis on site, violating the axle load assumptions.
  2. Crane downtime cascades. A 20-minute crane outage caused upstream appointment invalidations that the solver had to recompute in real-time under high Redis lock contention.

06 Run it yourself

$ git clone https://github.com/gospinup/spin-03-drayage
$ cd spin-03-drayage && pip install -r requirements.txt
$ python run_benchmark.py

07 Discuss

Every spin has a thread on GitHub Discussions. If a constraint looks over-fit, if the baseline is too easy, or if you’ve solved this properly in production and I’m busy reinventing it badly — say so there. Corrections get credited in the spin itself.

Open GitHub Discussion →