- Apollo by ClassicOldSong: https://github.com/ClassicOldSong/Apollo
Welcome to a small GUI that keeps Apollo/Sunshine wrangled on Windows. It builds one sandbox per profile, rewrites configs, and launches multiple Apollo/Sunshine instances on the same PC under the same Windows user—each instance isolated so your setup doesn’t dissolve into shared logs and shared sins.
A single instance of Sunshine is like a single cookie—useless once the family arrives. We automate the second, third, and yes… fifth cookie.
One machine, one user session, multiple independent Apollo/Sunshine processes:
- Instance A can be configured to stream Monitor 1.
- Instance B can be configured to stream Monitor 2.
- Instance C can be configured to stream Monitor 3. …and nobody tramples each other’s runtime files, ports, TLS keys, or logs.
(You’re not creating new Windows users or “seats” in the OS sense—just separate Apollo/Sunshine instances with separate runtimes.)
- One PC / One User / Many Instances – Run multiple Apollo/Sunshine instances under the same Windows user, each with its own isolated runtime.
- Multi-Monitor Friendly – Make separate instances and aim them at different screens (2nd/3rd/4th monitor) via per-profile settings/config.
- Profile Sandboxing – Each profile gets unique filenames (
apps_[slug].json,sunshine_state_[slug].json, etc.) insideC:\ProgramData\ApolloLauncher\runtime. - Per-Profile Config Rendering – Each profile writes its own rendered config (
<slug>.conf) so every instance can point at its own files, name, ports, and display settings. - Clean Slate Creation – On first launch we seed brand-new JSON with fresh salts, UUIDs, and empty app lists. If the files already exist, we leave them alone.
- Port-Family Aware Multi-Instance – Apollo/Sunshine
port = ...is a base port that expands into a family of TCP/UDP ports. The launcher checks the whole family before launch to avoid collisions. - Safe Base-Port Spacing – Base ports auto-space by
SUNSHINE_PORT_STRIDE(default:100) so multiple instances don’t fight over neighbors like48000/48010. - Correct Web UI Shortcut – “Open Web UI” uses
https://localhost:(base port + 1)(because the UI typically lives there). - Silent Launch – Hides the Sunshine console by default for current-user launches. Toggle it off if you miss blinking cursors.
- Tray / Background Mode – Optional system tray support:
--tray= close-to-tray behavior--background= start hidden in tray
- Easy Administrator Launch (Windows) –
--elevaterelaunches the launcher with UAC. Also includesApolloLauncher_Admin.cmdfor double-click convenience. - Dependency Auto-Heal – Missing
PySide6orpsutil? The launcher self-installs them viapipand tattles via MessageBox when the network is gone. - Logging – Every session logs to
C:\ProgramData\ApolloLauncher\logs\launcher_YYYYMMDD_HHMMSS.log. - Optional Runtime Cleanup – Deleting a profile politely asks if you want its runtime folder nuked too.
Command-line utilities are provided in the cli/ directory for automated control and monitoring.
Capture screenshot from specified monitor.
python cli/screenshot.py 0 # Monitor 0
python cli/screenshot.py 1 # Monitor 1Output: screenshot_monitor{N}_{TIMESTAMP}.png
Check status of all Apollo instances.
python cli/moonlight_status.pyOutput:
Apollo Instance Status:
----------------------------------------
Port 47990: [UP]
Port 48090: [UP]
Port 48190: [DOWN]
----------------------------------------
Total: 2/5 instances running
Launch all instances in headless mode.
python cli/launch_background.pySee cli/README.md for detailed documentation.
Stream Apollo instances to Moonlight clients over LAN using NVIDIA NVENC hardware encoding.
On Host PC (This One):
- Run GUI launcher:
python apollo_multi_launcher.py - Create profile per monitor (Screen1 → port 47989, Screen2 → 48089, etc.)
- Open
http://localhost:47990→ Configuration → Video:- Encoder: NVENC
- Codec: H.265/HEVC
- Bitrate: 50 Mbps (adjust for your LAN)
- Enable pairing for each instance
On Client PC (Moonlight 100.70.191.47):
- Open Moonlight Desktop
- Add host:
<HOST_IP>:47990 - Apollo shows PIN → enter in Moonlight
- Stream any profile → verify display appears
- Test mouse/keyboard input
| Instance | Port | Web UI | Display |
|---|---|---|---|
| Screen 1 | 47989 | 47990 | Monitor 0 |
| Screen 2 | 48089 | 48090 | Monitor 1 |
| Screen 3 | 48189 | 48190 | Monitor 2 |
Port stride: +100 per instance (configurable)
A reference configuration is available at configs/apollo-nvenc-template.conf. Copy and customize for your setup:
cp configs/apollo-nvenc-template.conf my-profile.conf
# Edit my-profile.conf with your bitrate, codecs, etc."Moonlight can't connect"
- Verify Apollo instances are running:
python cli/moonlight_status.py - Check firewall allows TCP/UDP on Apollo ports (47989+)
- Ensure client has network path to host
"Lag or frame drops"
- Reduce bitrate in Apollo settings
- Check LAN speed: target 50-100 Mbps for 1080p@60
- Switch to H.264 if H.265 unsupported on client
"Black screen after pairing"
- Verify monitor index matches Apollo profile
- Restart instance:
python apollo_multi_launcher.py→ right-click profile → Restart
- LAN: Host and client must be on same network (or routable IP)
- Bandwidth: 50 Mbps minimum for 1080p@60 (hardware dependent)
- Latency: <30 ms ideal (100+ ms starts feeling sluggish)
Control Apollo from Claude CLI using the moonlight-control skill.
# Check status
claude-code moonlight-control:status
# Take screenshot
claude-code moonlight-control:screenshot 0
# Launch instances
claude-code moonlight-control:launch
# List profiles
claude-code moonlight-control:list-profilesSee skills/moonlight-control/skill.md for full documentation.
To enable: set APOLLO_REPO environment variable:
export APOLLO_REPO=$(pwd)# Optional: use a virtualenv if you’re into healthy habits
python -m pip install --upgrade pip
python -m pip install PySide6 psutilThen launch the GUI:
python apolloluncher.pyIf you double-click the script without dependencies present, it will attempt to install them automatically and display a Windows message box when it can’t.
Want one Windows login to stream multiple monitors to multiple Moonlight clients?
- Hit New and name the profile (e.g.
Instance-1,Instance-2,Couch,Desk2,The Third Monitor™). - Launcher auto-detects
sunshine.exeandsunshine.conffromC:\Program Files\Apollo\(adjust in settings if yours lives elsewhere). - In the profile editor, pick the target monitor/display for this instance (2nd, 3rd, …) if your build supports it.
- Choose a base port (or let the launcher auto-space it).
- Launch the instance.
- Click Open Web UI → configure apps/settings for that instance.
- Repeat for each monitor you want to stream.
Result: multiple Apollo/Sunshine instances, all under the same Windows user, each mapped to its own runtime + ports (and optionally its own monitor).
- Hit New and name the profile.
- Launcher auto-detects
sunshine.exeandsunshine.conffromC:\Program Files\Apollo\. - Unique filenames are generated for
apps,state,credentials,log,pkey, andcert. - First launch writes clean JSON + empty log.
- Subsequent launches reuse whatever the instance created through the Web UI.
- Silent Launch (default): Sunshine runs without a console window; output goes to the per-profile log in runtime.
- Open Web UI: Opens
https://localhost:(base port + 1)for the selected instance. - Check Ports: Verifies the required TCP/UDP port family for that instance (not just a single port).
- Same User, Separate Instances: Each instance is isolated by profile/runtime—so you can run multiple instances without shared-state collisions.
- Run-As / Admin:
- By default, profiles launch as the current user.
- Use Edit to configure advanced options (paths, ports, display selection, console visibility, etc.).
- If you need Admin (firewall rules, privileged paths, etc.), launch the launcher with
--elevateor useApolloLauncher_Admin.cmd.
In Apollo/Sunshine, port = X is a base port, not “one port”. It fans out into a family of ports (TCP + UDP).
That means two instances with base ports that are “close” can still collide and fail to bind (classic symptoms: errors about 48000 / 48010 and friends).
The launcher helps by:
- checking the whole port family before launch
- spacing base ports using
SUNSHINE_PORT_STRIDE(default100) - suggesting a safe base port if something is already taken
If Windows Firewall is enabled, you may need to allow the required TCP/UDP ports for each instance base port.
# Tray icon + close-to-tray behavior
python apolloluncher.py --tray
# Start hidden in tray
python apolloluncher.py --background
# Relaunch the launcher as Administrator (UAC prompt)
python apolloluncher.py --elevateC:\ProgramData\ApolloLauncher\
├─ profiles.json # saved profiles
├─ logs\launcher_*.log # session logs
└─ runtime\<profile-slug>\
├─ apps_<slug>.json
├─ sunshine_state_<slug>.json
├─ credentials_<slug>.json
├─ sunshine_<slug>.log
├─ pkey_<slug>.pem
├─ cert_<slug>.pem
└─ <slug>.conf # rendered config
Delete a profile → choose whether to keep or remove this runtime folder.
| Symptom | Fix |
|---|---|
| “ModuleNotFoundError: PySide6” | Launcher auto-installs, but if offline run pip install PySide6. |
| “Couldn't bind … port 48000 / 48010 / etc.” | Your instances’ port families overlap. Increase the base port, rely on auto-spacing (SUNSHINE_PORT_STRIDE), or use Check Ports to get a safe suggestion. |
| Web UI looks “wrong” / stuck login page | Make sure you opened https://localhost:(base port + 1) for that instance. |
| Two instances “show the same screen” | Edit the profiles and ensure each one targets a different monitor/display setting, then relaunch. |
Q: Can we run more than two Suns?
A: Yes. Sunshine is the star; we’re just the stage crew.
Q: Can the same Windows user run multiple instances and stream multiple monitors at once?
A: Yep. That’s the whole point: multiple isolated instances under the same user, each with its own runtime + ports, and (optionally) its own target display.
Q: Why the jokes?
A: Because if you’re launching multiple headless GPUs at 3 AM, humor is the only thing preventing registry edits.
Q: Does this work on Linux/macOS?
A: Most logic is cross-platform, but the GUI focus and silent launch polish target Windows. Patches welcome.
- Fork, branch, send PR.
- Run
python -m compileall apolloluncher.pyandpython apolloluncher.py --self-testbefore pushing. - Attach a session log when reporting issues; it’s the difference between “funny bug” and “haunting bug”.
MIT-ish (check MDFILES/LICENSE.txt). Share, break, improve—just don’t pawn it off as your own without buying us coffee. (that was a joke XD) feel free to use it as you wish humans :]
apollo-multi-instance-launcher/
├── apollo_multi_launcher.py # Main GUI launcher
├── ApolloLauncher_Admin.cmd # Admin launcher shortcut
├── README.md # This file
├── cli/ # Command-line tools
│ ├── __init__.py
│ ├── screenshot.py # Screenshot capture
│ ├── moonlight_status.py # Instance health check
│ ├── launch_background.py # Headless launcher
│ └── README.md # CLI documentation
├── configs/ # Configuration templates
│ └── apollo-nvenc-template.conf # NVENC reference config
└── skills/ # Claude CLI integration
└── moonlight-control/
├── skill.md # Skill documentation
└── handler.py # Python skill handler
Runtime data: C:\ProgramData\ApolloLauncher\
- Apollo by ClassicOldSong: https://github.com/ClassicOldSong/Apollo
- This launcher made with <3 by neo0oen — across a purple neon label at the bottom-left of the window.
- Hazardous code clean-up performed by Codex CLI, powered by GPT-5-class LLMs that never sleep and seldom blink.
Stay weird, stay multi-instance.