Skip to content

Commit

Permalink
Merge pull request #122 from PierreBeucher/sunshine
Browse files Browse the repository at this point in the history
Sunshine streaming server support ☀️
  • Loading branch information
PierreBeucher authored Feb 2, 2025
2 parents 741fe25 + ee1e615 commit 43dd562
Show file tree
Hide file tree
Showing 95 changed files with 2,237 additions and 602 deletions.
16 changes: 13 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ARG NODE_VERSION="20.16.0-bookworm-slim"
ARG NODE_VERSION="22.13.1-bookworm-slim"
FROM node:${NODE_VERSION} AS build

RUN apt update && apt install -y \
Expand Down Expand Up @@ -50,12 +50,18 @@ FROM node:${NODE_VERSION}

# Global tooling
RUN apt update && apt install -y \
python3 \
ansible \
python3-pip \
curl \
ssh \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

# Ansible via pip to use fixed version for better reproducibility
# Can't use pipx yet for global install as major distrib have an older version (1.1.0)
# which does not support pipx ensurepath --global
# Python warns about break-system-packages but should be fine
RUN pip3 install ansible==11.2.0 --break-system-packages

# Required for Azure DefaultAzureCredential
RUN curl -sL https://aka.ms/InstallAzureCLIDeb -o install.sh && chmod +x install.sh && ./install.sh -y

Expand Down Expand Up @@ -85,6 +91,10 @@ RUN npm ci --omit dev
COPY --from=tsc /build/dist dist/
COPY LICENSE.txt .

# Temporary to remove deprecation warning
# Should be fixed ASAP with deps bump
ENV NODE_NO_WARNINGS=1

RUN npm install --global

ENTRYPOINT ["cloudypad"]
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
[![Discord](https://img.shields.io/discord/856434175455133727?style=for-the-badge&logo=discord&logoColor=ffffff&label=Chat%20with%20us&labelColor=6A7EC2&color=7389D8)](https://discord.gg/dCxDVfVnSD)
[![GitHub License](https://img.shields.io/github/license/PierreBeucher/cloudypad?style=for-the-badge&color=00d4c4)](./LICENSE.txt)

Cloudy Pad is a Free, Open Source alternative to GeForce Now, Blacknut and similar Cloud Gaming platforms. It lets you deploy a Cloud gaming server anywhere in the world and play your own games - without requiring a powerful gaming machine or a costly subscription:
Cloudy Pad is a Free, Open Source alternative to GeForce Now, Blacknut and similar Cloud Gaming platforms. It lets you deploy a Cloud gaming server like [Sunshine](https://github.com/LizardByte/Sunshine) anywhere in the world and play your own games - without requiring a powerful gaming machine or a costly subscription:

- Stream with **[Moonlight](https://moonlight-stream.org/)** client
- Run your games through **[Steam](https://store.steampowered.com/)**, **[Pegasus](https://pegasus-frontend.org/)** or **[Lutris](https://lutris.net/)**
- Deploy on **AWS**, **Google Cloud**, **Azure** or **Paperspace**
- **No screen or virtual screen needed.** Cloudy Pad manages it all for you !
- Setup automated **Cost alerts** to avoid overspending 💸
- Use **Spot instances** for up to **90% cheaper** instances
- Play **30 hours per month** for **~15$ / month or less**
- **Pay by the hour, no subscription** required
- Streaming server: choose between [Sunshine](https://github.com/LizardByte/Sunshine) and [Wolf](https://games-on-whales.github.io/wolf/stable/)

**Not familiar with Cloud Gaming ?** See [What's Cloud Gaming and how is Cloudy Pad useful ?](./docs/src/what-is-cloud-gaming.md)

Expand Down
12 changes: 6 additions & 6 deletions ansible/inventories/test.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
all:
hosts:
"172.83.9.94":
ansible_user: paperspace
vars:
docker_users:
- paperspace

"15.237.17.46":
ansible_user: ubuntu
# ansible_port: 47951
# vars:
# docker_users:
# - paperspace
4 changes: 3 additions & 1 deletion ansible/requirements.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ collections:
- name: community.docker
version: "3.10.4"
- name: community.general
version: "9.1.0"
version: "9.1.0"
- name: ansible.posix
version: "2.0.0"
31 changes: 31 additions & 0 deletions ansible/roles/nvidia-driver/tasks/container-toolkit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Install Nvidia Container Toolkit via apt
# From https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html
- name: Add NVIDIA GPG key for the Container Toolkit
ansible.builtin.apt_key:
url: https://nvidia.github.io/libnvidia-container/gpgkey
state: present
keyring: /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
register: nvidia_container_toolkit_gpg_key

- name: Add NVIDIA container toolkit repository
ansible.builtin.apt_repository:
repo: "deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://nvidia.github.io/libnvidia-container/stable/deb/$(ARCH) /"
filename: "nvidia-container-toolkit.list"
state: present
register: nvidia_container_toolkit_apt_repo

- name: Update apt cache
ansible.builtin.apt:
update_cache: yes
when: nvidia_container_toolkit_gpg_key.changed or nvidia_container_toolkit_apt_repo.changed

- name: Install NVIDIA Container Toolkit
ansible.builtin.apt:
name: nvidia-container-toolkit
state: present

- name: Update container runtime
command: nvidia-ctk runtime configure --runtime=docker

- name: Restart Docker daemon
command: systemctl restart docker
4 changes: 4 additions & 0 deletions ansible/roles/nvidia-driver/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,7 @@
reboot:
post_reboot_delay: 10
when: nvidia_driver_module_file_result.changed or nvidia_dev_systemd_unit.changed or nvidia_dev_script.changed

- name: install NVIDIA container toolkit
import_tasks: container-toolkit.yml
tags: [ nvidia-container-toolkit ]
2 changes: 1 addition & 1 deletion ansible/roles/prepare-install/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
become: true
file:
state: absent
path: /etc/apt/sources.list.d/paperspace.list
path: /etc/apt/sources.list.d/paperspace.list
27 changes: 27 additions & 0 deletions ansible/roles/sunshine/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Whether to enable Nvidia
# Assumes Nvidia drivers are installed
sunshine_nvidia_enable: true

sunshine_server_name: Sunshine

# Directory where Sunshine Docker Compose files and other configuration are copied
sunshine_project_dir: "{{ ansible_user_dir }}/sunshine"

# Directory where Sunshine and other data (eg. Steam game files) are persisted
# Bind mounted in Sunshine container for persistence
# Changing this value will cause to lose track of existing data unless they are moved as well
sunshine_data_dir: "{{ sunshine_project_dir }}/data"

# Set the Nvidia driver version to install additional components in container on startup
# If not set, Nvidia driver version will be inferred from host
# Set to force use of specific Nvidia driver version
# A mismatch between host version and container version may yield unexpected results
# sunshine_nvidia_driver_version:

# Sunshine Web UI username and password (base64 encoded)
sunshine_web_username: ""
sunshine_web_password_base64: ""

# Sunshine container image to use
sunshine_image_tag: "dev"
sunshine_image_repo: "ghcr.io/pierrebeucher/cloudypad/sunshine"
40 changes: 40 additions & 0 deletions ansible/roles/sunshine/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# ---
- name: Ensure Sunshine project directory exists
file:
path: "{{ sunshine_project_dir }}"
state: directory
mode: '0750'

- name: Ensure Sunshine data directory exists
become: true # required since container user may not match host user. TODO fix this
file:
path: "{{ sunshine_data_dir }}"
state: directory
mode: '0750'

- name: import Nvidia tasks
import_tasks: nvidia.yml
when: sunshine_nvidia_enable

# List of Compose files to use depending on GPU used
- name: Set Docker Compose files
set_fact:
sunshine_compose_files: "{{
['docker-compose.yml']
+ (['docker-compose.nvidia.yml'] if sunshine_nvidia_enable else [])
}}"

- name: Copy Docker Compose files
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ sunshine_project_dir }}/{{ item }}"
mode: '0644'
loop: "{{ sunshine_compose_files }}"

- name: Deploy Sunshine container
community.docker.docker_compose_v2:
project_src: "{{ sunshine_project_dir }}"
build: always
state: present
files: "{{ sunshine_compose_files }}"
pull: always
19 changes: 19 additions & 0 deletions ansible/roles/sunshine/tasks/nvidia.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
- name: Get current NVIDIA driver version
slurp:
src: /sys/module/nvidia/version
register: sunshine_nvidia_driver_version_file
ignore_errors: true # expected that version file may not yet exist if driver not installed

- name: Set NVIDIA driver version var
when: sunshine_nvidia_driver_version_file is success
set_fact:
sunshine_nvidia_driver_version: "{{ sunshine_nvidia_driver_version_file.content | b64decode | trim }}"

- name: Fail if Nvidia driver version can't be found
when: sunshine_nvidia_driver_version_file is not success
fail:
msg: Couldn't infer Nvidia driver version from host /sys/module/nvidia/version. Is Nvidia driver installed and loaded ?

- name: Show NVIDIA driver version
ansible.builtin.debug:
msg: "Found NVIDIA driver version: {{ sunshine_nvidia_driver_version }}"
16 changes: 16 additions & 0 deletions ansible/roles/sunshine/templates/docker-compose.nvidia.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Docker Compose override to enable Nvidia GPU in container
services:
cloudy:
runtime: nvidia
environment:
# Enable Nvidia to trigger specific behaviors in container
NVIDIA_ENABLE: true
# Nvidia driver version matching host driver
NVIDIA_DRIVER_VERSION: "{{ sunshine_nvidia_driver_version }}"
deploy:
resources:
reservations:
# Enable GPU in container
# See https://docs.docker.com/compose/how-tos/gpu-support/#enabling-gpu-access-to-service-containers
devices:
- capabilities: [gpu]
48 changes: 48 additions & 0 deletions ansible/roles/sunshine/templates/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
services:
cloudy:
image: {{ sunshine_image_repo }}:{{ sunshine_image_tag }}
container_name: cloudy
# Need generous shared memory size as some services (eg. Steam) require a lot
shm_size: "2G"
# Privileged required to run Steam with bwrap (sandboxing)
privileged: true
# Always start container on machine startup unless specifically stopped
restart: unless-stopped
# uinput is used by Susnhine to emulate input devices (keyboard, mouse, etc.)
devices:
- /dev/uinput
volumes:
# Allow input access from container (keyboard, mouse, etc
# TODO maybe not required from fully headless setup
- /dev/input:/dev/input
# Cloudy Pad data and XDG_DATA_HOME where data should be persisted (game files, Sunshine state, etc.)
- "{{ sunshine_data_dir }}:/cloudy/data"
# Allow container to read/write/manage attributes on character device 13
# 13 matches /dev/input mouse, keyboard, etc. devices
# thus allowing process in container to manage input which we need for Sunshine
device_cgroup_rules:
- 'c 13:* rmw'
ports:
- "47984:47984/tcp" # HTTPS
- "47989:47989/tcp" # HTTP
- "47990:47990/tcp" # Web
- "48010:48010/tcp" # RTSP

- "47998:47998/udp" # Video
- "47999:47999/udp" # Control
- "48000:48000/udp" # Audio
- "48002:48002/udp" # Mic (unused)
environment:
# Desired keyboard layout
# See available layouts: https://man.archlinux.org/man/xkeyboard-config.7#LAYOUTS
# TOTO support models, variants and options
#
# Passed to Xorg config as per https://wiki.archlinux.org/title/Xorg/Keyboard_configuration#Using_X_configuration_files
KEYBOARD_LAYOUT: fr

# Sunshine Web UI password
# If unset or empty, can be set via Web UI directly
# Replace $ by $$ to avoid unwanted variable interpolation by Compose (See https://docs.docker.com/reference/compose-file/interpolation/)
SUNSHINE_WEB_PASSWORD: {{ sunshine_web_password_base64 | b64decode | replace('$', '$$') | quote }}
SUNSHINE_WEB_USERNAME: {{ sunshine_web_username | quote }}
SUNSHINE_SERVER_NAME: {{ sunshine_server_name | quote }}
29 changes: 29 additions & 0 deletions ansible/sunshine.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Deploy Sunshine in container
# Use with -e sunshine_nvidia_enable=true for Nvidia GPU
---
- name: "Wait for machine to be reachable (timeout: 5min)"
hosts: all
gather_facts: false
tasks:
- wait_for_connection:
timeout: 300
sleep: 5

- name: Sunshine container
hosts: all
roles:
- role: geerlingguy.docker
tags: [ docker ]
become: true
docker_users:
- "{{ ansible_user_id }}"
- role: roles/nvidia-driver
tags: [ nvidia ]
become: true
when: sunshine_nvidia_enable
vars:
nvidia_driver_module_params: |
# nvidia-drm module is required by Wolf
options nvidia-drm modeset=1
- role: roles/sunshine
tags: [ sunshine ]
1 change: 1 addition & 0 deletions ansible/playbook.yml → ansible/wolf.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Deploy Wolf streaming server
- name: "Wait for machine to be reachable (timeout: 10min)"
hosts: all
gather_facts: false
Expand Down
Loading

0 comments on commit 43dd562

Please sign in to comment.