Self-hosted service log for machines and agricultural equipment — v1.11.0
Installable PWA with a mobile-first dark UI. Runs entirely in Docker, no cloud required.
| Service records | Date, client, hours, hour-meter, notes |
| Scheduling | Plan future appointments with a Scheduled status |
| Agenda | Monthly calendar with event chips, day detail panel, upcoming list |
| Hour-meter | Manual start/end with automatic delta |
| Billing | Separate operator and machine hourly rates, travel fee, discount, auto-total |
| Auto travel fee | Calculate road distance from your base to the client and apply a configurable fee formula |
| VAT | Optional per-service VAT rate with live net/gross display |
| Payment tracking | Pending / Paid per service |
| Tips | Counted in received, excluded from billed |
| Attachments | Attach photos, videos, PDFs and documents to any service |
| Clients | Name, address (with map picker), phone; edit client directly from the service form |
| Map picker | Pick client address on an OpenStreetMap map — no API key required |
| Summaries | Monthly and all-time, per client, net and gross; operator and machine cost breakdown |
| LubeLogger integration | Optional: shows the machine's all-time maintenance/fuel cost on the dashboard, pulled from a self-hosted LubeLogger instance |
| Additional statistics | Optional toggle (Settings) to show extra stats on the Summary — currently mean time per service |
| Invoices | Printable invoice generated from any service |
| Quotes (Orçamentos) | Build printable quotes, saved in a dedicated tab — edit, duplicate, set status (pending/accepted/rejected), convert to a service, or re-open the PDF |
| Export | CSV download |
| Settings | Full backup/restore (DB + files), language, theme, invoice details, storage stats |
| PWA | Installable on Android & iOS, works offline |
Prerequisites: Docker with the Compose plugin.
mkdir servilog && cd servilog
curl -O https://raw.githubusercontent.com/JorgeS15/ServiLog/main/docker-compose.yml
docker compose up -dServiLog is now running at http://localhost:4000.
The database and uploads folder are created automatically under ./data/.
Once you have a domain pointing to your server, you can install ServiLog as an app:
- Android (Chrome) — open the URL → menu ⋮ → "Add to Home Screen"
- iOS (Safari) — open the URL → share icon → "Add to Home Screen"
Settings → Download Backup exports a single .slb file containing the SQLite database and all uploaded files (photos, videos, documents).
Restore by selecting the .slb file (or a legacy .db file) in Settings → Load Backup.
Manual backup: copy ./data/ — restore by replacing the folder and restarting the container.
ServiLog can send an email reminder 7 days and 1 day before each scheduled service. Configure it via environment variables in docker-compose.yml:
environment:
- SMTP_HOST=smtp.gmail.com
- SMTP_PORT=587
- SMTP_USER=you@gmail.com
- SMTP_PASS=your-app-password # Gmail: use an App Password, not your account password
- SMTP_FROM=ServiLog <you@gmail.com>
- NOTIFY_EMAIL=you@gmail.com # where to send the reminders
- NOTIFY_TIME=08:00 # time of day to run the check (server local time)
- SMTP_SECURE=false # set true only for port 465All four of SMTP_HOST, SMTP_USER, SMTP_PASS, and NOTIFY_EMAIL must be set — notifications are silently disabled if any are missing.
Gmail tip: enable 2FA on your Google account, then generate an App Password to use as SMTP_PASS.
Track the machine's real maintenance/fuel cost alongside your service revenue. If you run a self-hosted LubeLogger instance for the same machine, ServiLog can show its all-time total cost (service + repair + upgrade + tax + gas records) right on the dashboard.
Configure it under Settings → LubeLogger Integration:
- Server address — e.g.
http://192.168.1.50:5000 - API key — generate one in LubeLogger under user settings
- Vehicle ID — the LubeLogger vehicle ID that corresponds to this machine
The API key is only ever used server-side — ServiLog's backend proxies the request, so the key never reaches the browser. Leave any of the three fields empty to disable the integration; the dashboard card stays hidden until all three are set.
ServiLog supports built-in password protection via the APP_PASSWORD environment variable. Set it in docker-compose.yml to enable a login page:
services:
servilog:
image: ghcr.io/jorges15/servilog:latest
container_name: servilog
restart: unless-stopped
volumes:
- ./data:/data
environment:
- DB_PATH=/data/tracker.db
- PORT=4000
- APP_PASSWORD=your-strong-password
ports:
- "4000:4000"When set, all routes (UI and API) require an authenticated session. Sessions are signed with HMAC-SHA256 and stored in an HttpOnly; SameSite=Strict cookie. Sign out is available at the bottom of the Settings page.
To add the Secure flag (recommended when serving over HTTPS), set HTTPS=true in your environment:
environment:
- APP_PASSWORD=your-strong-password
- HTTPS=trueIf APP_PASSWORD is not set, the app is open to anyone who can reach the port — suitable for local-network use, but not recommended for public exposure.
| Measure | Detail |
|---|---|
| Login rate limiting | Max 10 failed attempts per IP per 15-minute window; further attempts receive a 429 response |
| Session cookies | HttpOnly; SameSite=Strict — inaccessible to JavaScript and not sent on cross-site requests. Add HTTPS=true env var to also set the Secure flag |
| Content-Security-Policy | Restricts scripts to self + unpkg (Leaflet CDN), API connections to self + Nominatim/OSRM, images to OSM tiles — limits XSS exfiltration vectors |
| XSS prevention | All user data rendered into HTML is escaped through escapeHtml() (covers &, <, >, ", ') |
| SQL injection | Every query uses parameterised statements — no string interpolation into SQL |
| File upload allowlist | Attachments restricted to an explicit MIME type list; unsafe types (HTML, SVG, JS) are force-downloaded as application/octet-stream |
| CSV injection | Export prefixes formula-trigger characters (=, +, -, @) to prevent spreadsheet formula execution |
| Foreign key integrity | SQLite PRAGMA foreign_keys = ON enforced on every database open |
| Security headers | X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Content-Security-Policy on every response |
For additional hardening when self-hosting publicly, consider putting a reverse proxy (e.g. Caddy, Nginx) with HTTPS in front of the container.