Installation
Get FD Commander running on your server in about 5 minutes. Works on any Linux machine with 2 GB of RAM, including a Raspberry Pi 4 running off a battery pack at your Field Day site.
System Requirements
FD Commander is designed to run on modest hardware, the kind of gear you'd actually bring to a field site. The deploy script installs everything it needs, so you're starting from a bare OS.
| Component | Minimum | Recommended |
|---|---|---|
| Operating System | Ubuntu 22.04 / Debian 12 | Ubuntu 24.04 LTS or Raspberry Pi OS |
| RAM | 2 GB | 4 GB+ |
| Storage | 16 GB | 32 GB+ |
| Network | LAN access | Gigabit Ethernet |
You do not need to install PHP, MariaDB, Node.js, or any other dependencies yourself. The deploy script handles all of it. It installs PHP 8.4, MariaDB, Node.js 20, Composer, and FrankenPHP automatically.
Install Steps
Clone the Repository
Pull down the source code from GitHub.
Run the Deploy Script
One command. The script installs PHP 8.4, MariaDB, Node.js, Composer, and FrankenPHP. It compiles front-end assets, runs database migrations, seeds reference data (bands, modes, ARRL sections, bonus types), and creates systemd services for the web server, queue worker, scheduler, and WebSocket server.
Open It in a Browser
Navigate to your server's address from any device on the network. Register the first account (it becomes the System Administrator), create your event, and start configuring.
1. Clone the Repository
2. Run the Deploy Script
At minimum, you need to pass your server's domain name or IP address. The script will use sensible defaults for everything else.
The --domain flag tells the server what hostname or IP address it should respond to. This is the address your operators will type into their browsers to reach FD Commander. A few common choices:
fd.localorfdcommander.localif your router or the server's mDNS broadcasts.localhostnames (common on most home and field networks)- The server's static IP address, like
192.168.1.50, if you want something that works regardless of DNS - A real domain like
fd.yourclub.orgif you're deploying with SSL for remote access
You can change this later by re-running the deploy script with a different --domain value.
The script runs through 9 phases: detecting your OS, installing system packages, cloning and building the application, creating the database, configuring FrankenPHP, setting up SSL (if requested), creating systemd services, opening firewall ports, and caching the configuration. It logs everything to /var/log/fd-commander-deploy.log.
3. Open It Up
Once the script finishes, open a browser on any device connected to your LAN and navigate to the domain or IP you specified. By default the server runs on port 80 (HTTP). The first account you register automatically gets the System Administrator role.
Deploy Script Flags
The deploy script accepts several optional flags to customize the installation. Only --domain is required.
| Flag | Default | Description |
|---|---|---|
| --domain | (required) | Domain name or IP address for the server |
| --port | 80 (HTTP) / 443 (SSL) | Port for the web server |
| --ssl | off | Enable HTTPS. Uses Let's Encrypt by default |
| (none) | Email for Let's Encrypt certificate registration | |
| --ssl-cert | (none) | Path to a custom SSL certificate file |
| --ssl-key | (none) | Path to a custom SSL private key file |
| --db-name | fd_commander | MariaDB database name |
| --db-user | fd_commander | MariaDB username |
| --db-password | (generated) | MariaDB password. Auto-generated if omitted |
| --app-path | /var/www/fd-commander | Installation directory on the server |
| --reverb-port | 8080 | Port for the WebSocket server (Laravel Reverb) |
| --branch | main | Git branch to deploy |
| --repo-url | (GitHub) | Custom Git repository URL |
| --no-seeders | off | Skip database seeders (bands, modes, sections, etc.) |
| --dry-run | off | Print what would be done without making changes |
Example: Raspberry Pi on your LAN
This deploys on port 80 with HTTP. Operators connect by opening http://fd.local in any browser. If your network doesn't resolve .local hostnames, use the Pi's IP address instead.
Example: HTTPS with Let's Encrypt
Caddy (the web server bundled with FrankenPHP) handles Let's Encrypt certificate provisioning and auto-renewal. This requires the server to be reachable from the internet on ports 80 and 443 during setup.
First Event Setup
After installation, you'll register the first user account (which becomes the System Administrator), then set up your Field Day event. The dashboard shows a setup checklist that guides you through the process.
Create an Event
Go to Administration > Events and click Create Event. You'll set the event type (Field Day or Winter Field Day), your club callsign, ARRL section, operating class, number of transmitters, and the power sources you'll be using. The start and end times are pre-filled based on the official ARRL schedule.
Setup Checklist
The dashboard tracks four setup milestones:
Event configuration created
Your event exists with callsign, section, class, and power sources defined.
Equipment inventoried
At least one piece of equipment has been added to the system.
Stations set up
At least one station has been defined with band and mode assignments.
Shifts scheduled
Operator shifts have been created for your stations.
Invite Operators
The dashboard shows a registration link (e.g. http://fd.local/register) that you can share with your club. Operators visit the link, register with their callsign, and they're in the system. By default, new registrations get the Operator role. You can change registration mode and default roles in Settings.
User Roles
FD Commander ships with four built-in roles. Each role has a specific set of permissions that control what parts of the system a user can access. Roles are managed under Settings > Role Management.
| Role | What They Can Do |
|---|---|
| System Administrator | Full access to all settings, events, users, roles, stations, equipment, scheduling, safety, reports, and audit logs. Cannot log contacts by default (to keep the admin role distinct from operators). |
| Event Manager | Manage events, stations, equipment, schedules, bonuses, guestbook, safety checklists, photos, and view reports. The person running your Field Day operation. |
| Station Captain | Log and edit contacts, view and manage stations, manage equipment. Runs a single operating position and keeps their operators on task. |
| Operator | Log contacts, view stations, manage their own personal equipment catalog. The default role for new registrations. |
Permissions are fully customizable. You can modify what each role can do, or adjust individual user permissions to fit how your club actually operates.
Stations
Stations represent individual operating positions. Go to Event Management > Stations to create them. Each station gets a name, band/mode assignments, and equipment. During the event, operators select which station they're logging for, and FD Commander tracks contacts per station.
If you're running the same station setup from a previous event, use the Clone from Event button to copy your station definitions forward.
Volunteer Scheduling
The scheduling system lets you define shift blocks for each station and role, then lets operators self-sign-up for open slots. Go to Event Management > Schedule to see the operator view, or Administration > Manage Schedule to create and assign shifts.
Fill-status indicators show which shifts have gaps so you can spot coverage problems before they happen. Each operator sees a "My Shifts" view so they know when they're up. You can schedule any role, not just operators: safety officers, public information table, station captains.
Equipment Tracking
Every operator has a personal equipment catalog under Equipment > My Catalog where they add their own gear (radios, antennas, power supplies, etc.) with photos, make/model, serial numbers, and tags. Equipment can then be committed to an upcoming event and assigned to specific stations.
The Equipment > All User Catalogs view shows everything across all users. Equipment > Event Commitments shows what's been committed for the current event. Equipment moves through four statuses: committed, delivered, returned, and damaged.
Safety Checklists
FD Commander includes the 15-item ARRL Field Day safety checklist, which is worth 100 bonus points when completed. Go to Event Management > Site Safety to work through the checklist during setup, or Administration > Manage Safety Checklist to customize the items.
Each item tracks who completed it and when. The scoring page automatically reflects whether the safety bonus has been earned.
Contact Logging
Operators log contacts under Logging > View Log after selecting their station. The logging interface is a single-field entry flow: type the callsign, the exchange fields populate, and hit Enter. Dupe checks run in real time against every contact logged by every station on every band. Section and class validation catches typos before they're saved.
All contacts go into a shared database via WebSocket (Laravel Reverb), so every connected browser sees updates as they happen. The scoring dashboard, logbook, and band activity all reflect new contacts instantly.
Scoring
The scoring page (Event Management > Scoring) shows your live score using the ARRL Field Day formula:
QSO points are weighted by mode: CW and digital contacts are worth 2 points each, phone contacts are worth 1. Power multiplier is determined automatically from your configured power sources (5x for ≤5W natural power, 2x for ≤5W or 6-100W with commercial/generator, 1x for over 100W). Bonus points come from 12 ARRL categories, each worth 100 points (except NTS Messages Handled at 10 points each).
Bonus Categories
The scoring page lists all 12 categories with their status: verified, claimed, or unclaimed. Some bonuses are tracked automatically (like the safety checklist completion or visitor guestbook count). Others are claimed manually by the event manager (like media publicity or satellite QSO).
| Bonus | Points | Tracking |
|---|---|---|
| Emergency Power | 100 | Based on configured power sources |
| Media Publicity | 100 | Manual claim |
| Public Location | 100 | Manual claim |
| Information Booth | 100 | Manual claim |
| Social Media | 100 | Manual claim |
| Safety Officer | 100 | Automatic (safety checklist completion) |
| Natural Power QSOs | 100 | Manual claim |
| Agency/Official Visit | 100 | Manual claim |
| Satellite QSO | 100 | Manual claim |
| Section Manager Message | 100 | Automatic (NTS message tracking) |
| W1AW Field Day Bulletin | 100 | Automatic (W1AW bulletin form) |
| NTS Messages Handled | 10 each | Automatic (per radiogram logged) |
Visitor Guestbook
Field Day is a public event. The guestbook (Event Management > Guestbook) lets visitors sign in with just a name and optionally a callsign. No account or password needed. Hand someone a tablet and they sign themselves in.
Licensed hams and general public are tracked separately. Guest counts feed into the Public Information bonus point tracking. The event manager can view and manage all entries under Administration > Manage Guestbook.
NTS Messages
FD Commander tracks NTS radiogram handling: originated, relayed, and received. Each message logged counts toward the NTS Messages Handled bonus (10 points each). There are also dedicated forms for capturing the Section Manager message and the W1AW Field Day bulletin, which are separate 100-point bonuses.
ICS-213 General Message format is also supported and can be enabled under Settings > System Preferences > Enable ICS-213 Message Format.
Photos
Operators can upload photos from any connected device during the event. The gallery (Event Management > Gallery) stores everything on the local server with no cloud dependency. Photos are tagged with who uploaded them and when.
Cabrillo Export
When the event ends, go to Reports to generate a Cabrillo file ready to upload to the ARRL submission page. The export includes all your QSO records formatted to the Cabrillo specification for Field Day.
Offline Resilience
FD Commander has two layers of resilience. First, the entire platform runs on your LAN with zero internet dependency, fully air-gapped. Second, if a browser temporarily loses its connection to the server (someone trips over the ethernet cable, Wi-Fi drops out), contacts queue locally on the device. When the connection comes back, they sync automatically. A status indicator shows how many contacts are queued and when they've been synced.
Paper Log Transcription
If the server goes down and you fall back to paper for a while, FD Commander has a transcription feature that lets you enter those contacts after the fact with the correct timestamps. This way nothing is lost and your Cabrillo export stays complete. There is a configurable post-event grace period (default: 30 days) during which operators can still enter late contacts.
Network Setup
FD Commander runs on your local network. Operators connect by opening the server's address in any web browser. A typical field setup looks like this:
Server
A Raspberry Pi, laptop, or any Linux box connected to your field network via ethernet. Runs FrankenPHP on port 80 and Laravel Reverb (WebSocket) on port 8080.
Network
A router or access point providing your field LAN. Can be Wi-Fi, ethernet, or both. No internet connection needed.
Client Devices
Any device with a web browser: laptops, tablets, phones. Windows, Mac, Linux, Android, iOS. Connect to the field network and open the server's address.
Docker
FD Commander includes a Docker Compose setup for containerized deployment. The stack runs two services: an application container (web server, WebSockets, queue worker, scheduler) and a MySQL database container.
Quick Start
The setup script creates .env from .env.example and auto-generates all secrets (DB_PASSWORD, REVERB_APP_KEY, etc.). Run it again on an existing .env to fill in any missing secrets without overwriting existing values. The app will be available at http://localhost (or whichever port you set with APP_PORT).
Configuration
These environment variables in .env are used by docker-compose.yml:
| Variable | Default | Description |
|---|---|---|
| APP_PORT | 80 | Host port mapped to the container |
| DB_DATABASE | fd_commander | MySQL database name |
| DB_USERNAME | fd_commander | MySQL user |
| DB_PASSWORD | (required) | MySQL password (auto-generated by setup.sh) |
| DB_ROOT_PASSWORD | rootsecret | MySQL root password |
To run on a custom port:
Architecture
The app container runs four processes via supervisord:
| Process | Purpose |
|---|---|
| Octane (FrankenPHP) | HTTP server on port 80 |
| Reverb | WebSocket server (port 8080, internal) |
| Queue Worker | Background job processing |
| Scheduler | Scheduled tasks (runs every 60s) |
WebSocket connections reach Reverb through Caddy's reverse proxy at the /app path, so only port 80 is exposed to the network.
First Run
On first startup, the entrypoint script automatically waits for the database, runs all migrations, runs production seeders (event types, bands, modes, sections, roles, permissions, etc.), generates an APP_KEY if not set, generates Reverb WebSocket credentials if not set, and caches configuration, routes, views, and events. Seeders only run once. A marker file in the persistent storage volume prevents re-seeding on subsequent starts.
Data Persistence
Two named volumes store persistent data:
| Volume | Contents |
|---|---|
| app-storage | Uploaded files, logs, framework cache |
| mysql-data | Database files |
These survive docker compose down. Use docker compose down -v to remove them (this destroys all data).
HTTPS with a Reverse Proxy
The container serves plain HTTP on port 80. For HTTPS, place a reverse proxy (like Nginx) in front of it. The app includes trustProxies(at: '*') so X-Forwarded-Proto and X-Forwarded-For headers are respected automatically. When behind a TLS proxy, update your .env:
Managing Services
Rebuilding After Updates
The entrypoint runs php artisan migrate --force on every start, so new migrations are applied automatically.
Updating
FD Commander includes an update.sh script that pulls the latest code, updates dependencies, runs new database migrations, and restarts services.
Run this at home when preparing for the next event, not during the event itself.