No description
Find a file
2026-01-18 19:00:07 +01:00
.github init 2026-01-18 17:42:32 +01:00
.kamal init app 2026-01-18 12:26:40 +01:00
app everything as jobs 2026-01-18 19:00:07 +01:00
bin init app 2026-01-18 12:26:40 +01:00
config job logs 2026-01-18 18:50:31 +01:00
db job logs 2026-01-18 18:50:31 +01:00
lib add spawn start stop buttons 2026-01-18 18:43:37 +01:00
log init app 2026-01-18 12:26:40 +01:00
public init app 2026-01-18 12:26:40 +01:00
script init app 2026-01-18 12:26:40 +01:00
storage init app 2026-01-18 12:26:40 +01:00
test everything as jobs 2026-01-18 19:00:07 +01:00
tmp init app 2026-01-18 12:26:40 +01:00
vendor init app 2026-01-18 12:26:40 +01:00
.dockerignore init app 2026-01-18 12:26:40 +01:00
.gitattributes init app 2026-01-18 12:26:40 +01:00
.gitignore init app 2026-01-18 12:26:40 +01:00
.rubocop.yml init app 2026-01-18 12:26:40 +01:00
.ruby-version init app 2026-01-18 12:26:40 +01:00
ARCHITECTURE.md config and params are text fields 2026-01-18 18:32:07 +01:00
config.ru init app 2026-01-18 12:26:40 +01:00
Dockerfile init app 2026-01-18 12:26:40 +01:00
Gemfile init 2026-01-18 17:42:32 +01:00
Gemfile.lock init 2026-01-18 17:42:32 +01:00
Rakefile init app 2026-01-18 12:26:40 +01:00
README.md init 2026-01-18 17:42:32 +01:00

L4D Tools

A Rails 8.1 application for managing Left4Dead 2 game servers with a web interface.

Features

  • Steam Authentication: Login via Steam OpenID
  • Server Templates: Define reusable server configurations with overlays, config options, and startup parameters
  • Overlays Management: Support for both system and custom overlays with file uploads
  • Server Lifecycle: Spawn, start, stop, restart, and delete L4D2 servers
  • Live Logs: Stream server logs in real-time via WebSocket (ActionCable)
  • Health Checks: Periodic monitoring of server status via systemd and RCON
  • Activity Logging: Track user actions (server creation, deletion, start/stop)
  • User-Level Systemd: Servers run as systemd user units (steam user)

System Requirements

  • Ruby 4.0+
  • Rails 8.1+
  • SQLite 3.8.0+
  • Linux (for L4D2 server and systemd support)
  • Left4Dead 2 base installation at /opt/l4d2/installation/
  • Overlays in /opt/l4d2/overlays/
  • fuse-overlayfs installed and available

Setup Instructions

1. Install Dependencies

bundle install

2. Database Setup

bin/rails db:migrate

3. Enable Systemd User Services (One-Time Setup)

Allow the steam user to run persistent systemd services:

loginctl enable-linger steam

Without this, user-level systemd services will stop when the user logs out.

4. Create Systemd Helper Scripts

Create /opt/l4d2/bin/ directory and add helper scripts for server lifecycle:

mkdir -p /opt/l4d2/bin

Create /opt/l4d2/bin/start-server (referenced by systemd units):

#!/bin/bash
SERVER_ID=$1
exec /opt/l4d2/servers/${SERVER_ID}/merged/srcds_run -norestart -pidfile /opt/l4d2/servers/${SERVER_ID}/pid -game left4dead2 -ip 0.0.0.0 -port $(grep "^Port:" /opt/l4d2/servers/${SERVER_ID}/server.cfg | cut -d' ' -f2) +hostname "Server" +map c1m1_hotel

Create /opt/l4d2/bin/stop-server:

#!/bin/bash
SERVER_ID=$1
# Systemd handles killing the process
exit 0

Make scripts executable:

chmod +x /opt/l4d2/bin/start-server /opt/l4d2/bin/stop-server

5. Configure Steam API Key (Optional for Development)

For Steam authentication in production, set the Steam API key:

export STEAM_API_KEY="your-steam-api-key"

In development, a test key is used by default.

6. Run the Application

Development:

bin/dev

This starts:

  • Rails server on http://localhost:3000
  • Solid Queue job processor (in Puma)
  • Asset pipeline watcher

Architecture

Domain Models

  • User — Steam-authenticated users
  • Overlay — Filesystem directories layered via overlayfs (system or custom)
  • ServerTemplate — Reusable server configuration (overlays + config + params)
  • Server — Active instance of a ServerTemplate
  • ConfigOption — Key-value server configuration (server.cfg)
  • StartupParam — Command-line arguments passed to srcds_run
  • Activity — Audit log of user actions

Workflow

  1. Create ServerTemplate

    • Select overlays (in priority order)
    • Define config options (key=value for server.cfg)
    • Define startup parameters
  2. Spawn Server from Template

    • User specifies: server name, port, optional parameter overrides
    • Rails generates /opt/l4d2/servers/{id}/server.cfg
    • Mounts overlayfs stack using fuse-overlayfs
    • Creates systemd user unit at ~/.config/systemd/user/left4dead2-{id}.service
    • Starts service via systemctl --user start
  3. Monitor Server

    • StatusUpdateJob polls every 30 seconds
    • Checks systemd status + RCON health
    • Updates Server.status and last_health_check_at
  4. Manage Server

    • Start/Stop/Restart via UI buttons
    • Stream live logs via ActionCable + journalctl
    • Delete server (stops service, removes config, unmounts overlayfs)

Directory Structure

/opt/l4d2/
├── installation/          # L4D2 base game files (read-only lower layer)
├── overlays/              # Overlay directories
│   ├── system_overlay_1/  # System overlays (created by setup.sh)
│   ├── custom_{id}/       # Custom overlays (user-created)
├── servers/               # Active server instances
│   └── {server_id}/
│       ├── server.cfg     # Generated config
│       ├── upper/         # Overlayfs writable layer
│       ├── work/          # Overlayfs work directory
│       ├── merged/        # Overlayfs mount point (active game filesystem)
│       └── pid            # Process ID file
├── configs/               # (Legacy - configs now live in /opt/l4d2/servers/{id}/)
└── bin/
    ├── start-server       # Systemd ExecStart script
    └── stop-server        # Systemd ExecStop script

Key Libraries

  • L4dServer::ConfigGenerator — Renders server.cfg from ConfigOptions
  • L4dServer::Launcher — Mounts overlayfs, generates systemd unit
  • L4dServer::SystemdManager — Wraps systemctl commands
  • L4dServer::HealthChecker — Monitors server health via systemd + RCON

Background Jobs

  • SpawnServerJob — Executed when user spawns server (mounts, starts)
  • StatusUpdateJob — Recurring job (every 30s) polling server status

WebSocket

  • LogChannel — Streams journalctl output to connected clients in real-time

Development Notes

Running Tests

bin/rails test

Console Access

bin/rails console

Viewing Logs

journalctl --user -u left4dead2-{server_id}.service -f

Database Migrations

bin/rails db:migrate              # Apply pending migrations
bin/rails db:rollback             # Revert last migration

Configuration

Environment Variables

  • STEAM_API_KEY — Steam OpenID API key (optional, test key used in dev)
  • RAILS_ENV — Rails environment (development/production)
  • RAILS_MASTER_KEY — Encryption key for credentials (production only)

Rails Credentials

bin/rails credentials:edit

Store sensitive data here (encrypted in Git).

Deployment (Kamal)

Docker deployment via Kamal:

kamal deploy

See config/deploy.yml for Kamal configuration.

Important: Ensure RAILS_MASTER_KEY is set in production environment.

Known Limitations / Future Work

  1. Single-Machine Only — Servers must run on same machine as Rails app
  2. No Backup/Restore — Server state is ephemeral; no state snapshots yet
  3. Basic Health Checks — RCON health check is simple; doesn't parse game state
  4. Manual Setup Scriptssetup.sh and wrapper scripts must be installed manually
  5. No API — Web UI only; no REST/GraphQL API yet

Contributing

Follow Rails conventions and the style guide:

bundle exec rubocop -a       # Auto-fix style issues
bundle exec brakeman         # Security scanning
bundle exec bundler-audit    # Gem vulnerability check

License

[Add your license here]