A self-hosted web application for independent delivery couriers to track shifts, earnings, and vehicle maintenance. Built with Rust and SurrealDB, LastMile helps drivers stay on top of their finances and vehicle health without relying on third-party services or tedious spreadsheets.
LastMile was born out of necessity. When circumstances led me to food delivery work to make ends meet, I found myself struggling with the same problems many gig workers face: tracking earnings, hours, and vehicle maintenance across shifts was time-consuming and cumbersome. Most solutions either cost money I didn't want to spend, compromised privacy, or simply didn't fit the flexible nature of delivery work. As a developer, I had the skills to build something that worked better for me, so I did. LastMile is a self-hosted, privacy-focused tool that gives drivers complete control over their data, with no subscriptions or third-party dependencies, and it provides confidence come tax season you have work data you can trust.
- Shift Management - Track start/end times, odometer readings, earnings, tips, and gas costs
- Real-time Statistics - View earnings, hours worked, average hourly rate, and total miles driven
- Flexible Filtering - Filter shifts by month, all time, or custom date ranges
- Maintenance Tracking - Set up mileage-based maintenance reminders for oil changes, tire rotations, and more
- Inline Editing - Click any field in the table to edit shift details on the fly
- CSV Export - Export shift data with automatic filtering based on your current view (month, all time, or custom range) for external analysis or tax preparation
- Dark Mode - Easy on the eyes during late-night shifts
- Fully Self-Hosted - Your data stays on your machine, no cloud required
- Responsive Design - Works on desktop and mobile devices
Try LastMile in your browser:
Launch Demo
Read more about Demo Mode.
- Rust 1.88 or higher
- Modern web browser (Chrome, Firefox, Safari, Edge)
-
Clone the repository:
git clone https://github.com/dylan-park/LastMile.git cd lastmile -
Run the application:
cargo run --release
-
Open your browser to http://localhost:3000
The database will be created automatically in the ./data directory.
Build and run with Docker:
docker build -t lastmile .
docker run -d \
--name lastmile \
--hostname lastmile \
--restart unless-stopped \
-p 3000:3000 \
-v ./data:/app/data \
-v ./static:/app/static:ro \
-e DATABASE_PATH=/app/data \
-e PORT=3000 \
-e TZ=America/Chicago \
-e RUST_LOG=info \
lastmileAccess the application at http://localhost:3000
docker build -t lastmile .
docker run -d \
--name lastmile-demo \
--hostname lastmile-demo \
--restart unless-stopped \
-p 3000:3000 \
-v ./static:/app/static:ro \
-e PORT=3000 \
-e TZ=America/Chicago \
-e RUST_LOG=info \
lastmile --demoRead more about Demo Mode.
For the easiest deployment:
docker build -t lastmile .
docker compose up lastmile -dThe application will be available at http://localhost:3000 with persistent data storage.
See docker-compose.yaml for configuration options.
docker build -t lastmile .
docker compose up lastmile-demo -dRead more about Demo Mode.
Environment variables (all optional):
| Variable | Description | Default |
|---|---|---|
DATABASE_PATH |
Database storage location | ./data |
PORT |
Application port | 3000 |
TZ |
Timezone for logs | America/Chicago |
RUST_LOG |
Log level (error, warn, info, debug, trace) |
info |
--e2e: Enables the/api/test/teardownendpoint. Only use this for e2e testing.--demo: Runs LastMile in Demo Mode
You can run the application in demo mode with:
cargo run --release -- --demoWhen enabled, demo mode changes the application behavior in the following ways:
- Uses an in-memory database instead of disk storage
- Creates isolated sessions per browser session
- Generates random sample data
- Automatically clears sessions that have been idle for more than 1 hour (checked hourly)
Demo mode is intended for showcasing the features of LastMile before committing to a full setup. All functionality is available except for persistent data storage.
If you have SurrealDB CLI installed, you can backup your database:
surreal export --endpoint file://./data --namespace lastmile --database main export.surql%%{init: {
"flowchart": {
"defaultRenderer": "elk",
"curve": "linear",
"nodeSpacing": 80,
"rankSpacing": 100
}
}}%%
flowchart LR
%% ---------------- FRONTEND ----------------
subgraph Frontend["Frontend (Vanilla JS)"]
direction TB
HTML[index.html]
CSS[styles.css - Styling]
UI[ui.js - UI Components]
APP[app.js - State Management]
API_CLIENT[api.js - API Client]
UTILS[utils.js - Utilities]
end
%% ---------------- BACKEND ----------------
subgraph Backend["Backend (Rust + Axum)"]
direction TB
MAIN[main.rs - Binary Entry]
MIDDLEWARE[middleware.rs - Session Middleware]
STATE[state.rs - App State]
SEEDING[seeding.rs - Sample Data]
HANDLERS[handlers/ - Route Handlers]
MODELS[models.rs - Data Models]
VALIDATION[validation.rs - Input Validation]
CALCULATIONS[calculations.rs - Business Logic]
end
%% ---------------- DATABASE ----------------
subgraph Database["Database Layer"]
direction TB
DB_SETUP[db/mod.rs - Schema Setup]
DB_HELPERS[db/helpers.rs - Query Helpers]
SURREALDB[(SurrealDB + RocksDB/Memory)]
end
%% ---------------- TESTING ----------------
subgraph Testing["Testing"]
direction TB
INTEGRATION[tests/handler.rs - Integration Tests]
TEST_HELPER[tests/helper.rs - Query Helpers Tests]
E2E[scripts/e2e.py - E2E Tests]
end
%% ---------------- FLOW ----------------
Frontend --> MAIN
MAIN --> MIDDLEWARE
MAIN --> STATE
MAIN --> SEEDING
MAIN --> HANDLERS
MAIN --> DB_SETUP
HANDLERS --> STATE
HANDLERS --> MODELS
HANDLERS --> VALIDATION
HANDLERS --> CALCULATIONS
HANDLERS --> DB_HELPERS
DB_HELPERS --> SURREALDB
DB_SETUP --> SURREALDB
STATE --> SURREALDB
E2E -.-> Frontend
INTEGRATION -.-> HANDLERS
TEST_HELPER -.-> DB_HELPERS
- Backend: Rust with Axum web framework
- Database: SurrealDB with embedded RocksDB backend
- Frontend: Vanilla JavaScript, HTML5, CSS3 (no frameworks)
Contributions are welcome! Here's how you can help:
- Fork the repository and create a new branch for your feature or bugfix
- Write tests for your changes (unit tests in their related modules, integration tests in
tests/, E2E tests inscripts/e2e.py) - Follow the existing code style:
- Rust: Use
cargo fmtandcargo clippy - JavaScript: 2-space indentation, double quotes
- Rust: Use
- Submit a pull request with a clear description of your changes
Unit/Integration Tests:
cargo testE2E Tests (requires Selenium Grid on port 4444):
# Optionally run in a venv
pip install -r requirements.txt
pytest scripts/e2e.py -vNote: E2E tests require the backend to be running with the --e2e flag to enable the database teardown endpoint:
cargo run --release -- --e2e- Improve CSS styling rules
- Focus more on mobile experience
- Improve table view
- Investigate desktop site improvements
- Improve table scrolling
- Focus more on mobile experience
- Improve tests
- Make E2E tests preserve original database
- Save test outputs to .log files so actions script can upload artifacts on failure
- Change
EarningstoFarethroughout application to reduce confusion
This project is dual-licensed under either:
- MIT License (LICENSE-MIT or http://opensource.org/licenses/MIT)
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.
Built with ❤️ for delivery drivers who want to take control of their data.

