The SFS Console is an optional feature that allows you to execute Statistics for Strava Symfony console commands directly from the web interface.
The SFS Console provides a terminal-like UI for running administrative commands without needing SSH access to your server or Docker exec commands. It's useful for:
- Building Statistics for Strava data files
- Importing activity data from Strava
- Managing webhook subscriptions
- Running maintenance tasks
The SFS Console uses a two-container security model to safely execute commands:
┌─────────────────────┐ ┌──────────────────┐ ┌──────────────────────┐
│ Config Tool UI │────>│ stats-cmd-runner │────>│ stats-cmd-helper │
│ (web interface) │ │ (validates cmds) │ │ (executes via exec) │
└─────────────────────┘ └──────────────────┘ └──────────────────────┘
- Purpose: Validates commands and handles request/response flow
- Security: No Docker socket access
- Validates commands against YAML allowlist before forwarding
- Provides health check endpoint
- Purpose: Executes validated commands via
docker exec - Security: Has Docker socket access but only accepts pre-validated commands
- Runs alongside the Statistics for Strava container
- Captures output to log files
- The runner validates commands from a YAML whitelist but has no execution privileges
- The helper has Docker socket access but only accepts pre-validated commands from the runner
- This separation prevents arbitrary command execution even if the web interface is compromised
Before enabling the SFS Console, ensure you have:
- Statistics for Strava running in Docker
- Config Tool already set up and working
- Access to your
docker-compose.ymlfile - Understanding of your Docker volume paths
Important: This MUST be done BEFORE starting containers. If the file doesn't exist when you start containers, Docker will create a directory instead, causing errors.
# Linux/Mac
cp console-commands.yaml.example ./config/settings/console-commands.yaml
sudo chown 1000:1000 ./config/settings/console-commands.yaml
# Windows (Docker Desktop)
copy console-commands.yaml.example ./config/settings/console-commands.yamlThe example file contains the default set of safe commands. See console-commands.yaml.example for the default configuration.
Add both services to your docker-compose.yml:
services:
# ... your existing services ...
# SFS Console - Command Runner (validates and proxies commands)
stats-cmd-runner:
image: ghcr.io/dschoepel/stats-cmd-runner:latest
container_name: stats-cmd-runner
restart: unless-stopped
working_dir: /var/www
command: ["node", "server.js"]
environment:
- TZ=${TZ}
- USERMAP_UID=${USERMAP_UID}
- USERMAP_GID=${USERMAP_GID}
- HELPER_URL=http://stats-cmd-helper:${STATS_CMD_HELPER_PORT:-8081}
- LOG_FILE=/var/log/stats-cmd-runner/runner.log
volumes:
- ./config/settings/console-commands.yaml:/var/www/console-commands.yaml:ro
- ./stats-cmd-runner-logs:/var/log/stats-cmd-runner
ports:
- "8093:8080"
networks:
- statistics-for-strava-network
# SFS Console - Command Helper (executes commands in SFS container)
stats-cmd-helper:
image: ghcr.io/dschoepel/stats-cmd-helper:latest
container_name: stats-cmd-helper
restart: unless-stopped
working_dir: /var/www
command: ["node", "server.js"]
environment:
- TZ=${TZ}
- PORT=${STATS_CMD_HELPER_PORT:-8081}
- TARGET_CONTAINER=statistics-for-strava
- COMMAND_LOGS_DIR=/var/log/stats-cmd/command-logs
- LOG_FILE=/var/log/stats-cmd/helper.log
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./config/settings/console-commands.yaml:/var/www/console-commands.yaml:ro
- ./stats-cmd-logs:/var/log/stats-cmd/command-logs:rw
networks:
- statistics-for-strava-networkAdd the runner URL environment variable to your config-manager service:
config-manager:
image: ghcr.io/dschoepel/stats-for-strava-config-tool:latest
# ... existing configuration ...
environment:
- STATS_CMD_RUNNER_URL=${STATS_CMD_RUNNER_URL:-http://stats-cmd-runner:8080}All containers must use the same USERMAP_UID and USERMAP_GID values from your .env file (default: 1000:1000). This ensures:
- Log directories are created with correct ownership
- Files can be read/written by all services
- No permission errors occur
# Linux/Mac
mkdir -p ./stats-cmd-logs ./stats-cmd-runner-logs
sudo chown 1000:1000 ./stats-cmd-logs ./stats-cmd-runner-logs
# Windows (Docker Desktop)
mkdir stats-cmd-logs
mkdir stats-cmd-runner-logsdocker compose up -d- Open the Config Tool web interface
- Go to Settings (gear icon)
- Navigate to User Interface Settings
- Toggle "Enable SFS Console" ON
- Save settings
- Navigate to Utilities > SFS Console in the sidebar
- Select a command from the dropdown
- Add parameters if needed (some commands accept arguments)
- Click Run
- Watch the real-time output in the terminal panel
- View past command executions with status badges
- Success (green), Failed (red), Stopped (orange), Running (yellow)
- Click to view logs from previous executions
- Rerun previous commands with one click
- View, download, or delete command execution logs
- Logs are stored in
stats-cmd-logs/directory - Access via the "Manage Logs" button in the history panel
- Click the Stop button during execution
- The running PHP process is terminated immediately
- Status shows "Stopped" in command history
The default allowlist includes these commands:
| Command | Description |
|---|---|
app:strava:build-files |
Build Statistics for Strava data files |
app:strava:import-data |
Import activity data from Strava |
app:strava:webhooks-create |
Create Strava webhook subscriptions |
app:strava:webhooks-unsubscribe |
Remove webhook subscriptions |
app:strava:webhooks-view |
View current webhook subscriptions |
Edit your console-commands.yaml file to add or modify commands:
commands:
custom-command:
name: "My Custom Command"
command: ["php", "bin/console", "app:custom:command"]
description: "Description of what this command does"
acceptsArgs: true
argsDescription: "Optional parameters description"
argsPlaceholder: "--option value"After editing, restart the runner container:
docker compose restart stats-cmd-runner- Ensure SFS Console is enabled in Settings > User Interface Settings
- Check that runner service is running:
docker ps | grep stats-cmd-runner
- Verify runner container is healthy:
docker compose ps - Check runner logs:
docker logs stats-cmd-runner - Ensure
STATS_CMD_RUNNER_URLis set correctly in config-tool service
- Check helper container logs:
docker logs stats-cmd-helper - Verify the target container name is correct (
CONTAINER_NAMEenv var) - Ensure the Statistics for Strava container is running
- Verify UID/GID consistency across all containers
- Check log directory ownership:
ls -la ./stats-cmd-logs ./stats-cmd-runner-logs - Ensure
.envfile has correctUSERMAP_UIDandUSERMAP_GID
This means LOG_FILE is pointing to a path not covered by a mounted volume. The node user inside the container cannot create new directories under /var/log/ (owned by root).
Solution: Ensure both LOG_FILE env vars are set and match the mounted volume paths:
- Runner:
LOG_FILE=/var/log/stats-cmd-runner/runner.log(maps to./stats-cmd-runner-logs) - Helper:
LOG_FILE=/var/log/stats-cmd/helper.log(maps to the parent of./stats-cmd-logs)
If you omit LOG_FILE, the server defaults to /var/log/strava-runner/ or /var/log/strava-helper/, which are not mounted and not writable.
- This happens if
console-commands.yamldidn't exist when containers started - Stop containers:
docker compose down - Remove the directory:
rm -rf ./config/settings/console-commands.yaml - Create the file properly (Step 1 above)
- Restart:
docker compose up -d
- Long-running commands may hit default timeouts
- Check command output for progress
- Consider running heavy operations during off-peak times
- Runner and helper communicate over the Docker network
- No external access to helper service
- Runner port (8093:8080) can be internal-only if not needed externally
- Only commands in
console-commands.yamlcan be executed - Review the allowlist periodically
- Don't add dangerous commands (e.g.,
rm, shell access)
- Only the helper container has Docker socket access
- This is necessary for
docker execfunctionality - Helper only accepts validated commands from runner
To completely disable the feature:
- Toggle OFF in Settings > User Interface Settings
- Optionally, remove or comment out the runner and helper services in
docker-compose.yml - Restart if you modified docker-compose.yml
When disabled, the SFS Console sidebar item is hidden and the console page shows setup instructions if accessed directly.
- Features Guide - Learn about other features
- Troubleshooting - More troubleshooting tips
- Installation Guide - General setup help