Initial Wine Desktop image and Pterodactyl Egg

This commit is contained in:
Lexian-droid
2026-07-30 00:14:23 +02:00
commit ef14c843f8
7 changed files with 391 additions and 0 deletions
+52
View File
@@ -0,0 +1,52 @@
name: Build and publish Docker image
on:
push:
branches: [main]
tags: ['v*']
workflow_dispatch:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=sha,format=long
type=ref,event=tag
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
+6
View File
@@ -0,0 +1,6 @@
.env
.idea/
.vscode/
*.swp
*.log
docker-compose.override.yml
+59
View File
@@ -0,0 +1,59 @@
FROM ghcr.io/ptero-eggs/yolks:wine_latest
LABEL org.opencontainers.image.title="Wine Desktop" \
org.opencontainers.image.description="Wine with an XFCE desktop, VNC, and noVNC for Pterodactyl" \
org.opencontainers.image.source="https://git.lexian.dev/Lexian-droid/OpenClaw-pterodactyl-wine-gui-egg"
USER root
ENV DEBIAN_FRONTEND=noninteractive \
DISPLAY=:0 \
DISPLAY_WIDTH=1280 \
DISPLAY_HEIGHT=720 \
DISPLAY_DEPTH=24 \
VNC_PORT=5900 \
NOVNC_PORT=6080 \
VNC_PASSWORD= \
XVFB=0 \
WINEPREFIX=/home/container/.wine
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
xfce4 \
xfce4-terminal \
xvfb \
x11vnc \
novnc \
websockify \
dbus-x11 \
xauth \
x11-utils \
x11-xserver-utils \
libgl1 \
libgl1-mesa-dri \
libegl1 \
mesa-utils \
pulseaudio \
pulseaudio-utils \
fonts-dejavu \
fonts-liberation \
fonts-noto-core \
fonts-noto-cjk \
winbind \
cabextract \
unzip \
curl \
wget \
&& rm -rf /var/lib/apt/lists/*
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
RUN chmod 0755 /usr/local/bin/docker-entrypoint.sh \
&& mkdir -p /home/container \
&& chown -R container:container /home/container 2>/dev/null || true
EXPOSE 5900/tcp 6080/tcp
# Keep the official tini process and inherited CMD. The wrapper prepares the
# desktop and then execs the official /entrypoint.sh command unchanged.
ENTRYPOINT ["/usr/bin/tini", "-g", "--", "/usr/local/bin/docker-entrypoint.sh"]
CMD ["/bin/bash", "/entrypoint.sh"]
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Lexian-droid
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+82
View File
@@ -0,0 +1,82 @@
# Wine Desktop
Wine Desktop extends the official Pterodactyl Wine yolk with a persistent virtual XFCE desktop. It is intended for Windows GUI applications that need a display, VNC access, or browser-based noVNC access inside a Pterodactyl or Pelican server.
## Features
- Based on `ghcr.io/ptero-eggs/yolks:wine_latest`.
- XFCE on Xvfb with configurable resolution.
- Direct VNC on TCP `5900` and browser VNC on TCP `6080`.
- Persistent Wine prefix at `/home/container/.wine`.
- PulseAudio, Mesa, fonts, winetricks, winbind, and common archive tools.
- Retains the official yolk's Steam update, Gecko/Mono, winetricks, and startup handling.
## Architecture
The image keeps the yolk's `/usr/bin/tini -g --` process and inherited command. Its entrypoint starts Xvfb, D-Bus, XFCE, x11vnc, noVNC, and the Wine prefix once, then executes the inherited `/bin/bash /entrypoint.sh`. The official entrypoint continues to expand `STARTUP`, perform optional updates and winetricks actions, and launch the application.
The Wine prefix is not deleted or rebuilt when the container restarts. Store the server data in the normal Pterodactyl volume to keep it persistent.
## Installation
Build locally:
```bash
git clone https://git.lexian.dev/Lexian-droid/OpenClaw-pterodactyl-wine-gui-egg.git WineDesktop
cd WineDesktop
docker build -t wine-desktop:latest .
```
The image exposes `5900/tcp` and `6080/tcp`. Publish those ports if running Docker directly:
```bash
docker run --rm -it -p 5900:5900 -p 6080:6080 \
-e STARTUP='wine /home/container/application.exe' \
wine-desktop:latest
```
For Pterodactyl or Pelican, import `egg-wine-desktop.json` into the nest, select the published image (or add your own registry tag), and create a server with the Wine Desktop Egg. Add allocations for ports 5900 and 6080 when your panel supports additional allocations.
## Startup and variables
The Egg's default startup is `wine {{SERVER_EXECUTABLE}}`, with `SERVER_EXECUTABLE` defaulting to `application.exe`. Set it to a path such as `MyApp/MyApp.exe` or use a custom startup command when needed. The underlying yolk continues to receive and evaluate its normal `STARTUP` value.
Useful variables:
| Variable | Default | Purpose |
| --- | --- | --- |
| `SERVER_EXECUTABLE` | `application.exe` | Executable used by the Egg startup command |
| `DISPLAY_WIDTH` | `1280` | Virtual screen width |
| `DISPLAY_HEIGHT` | `720` | Virtual screen height |
| `WINEPREFIX` | `/home/container/.wine` | Persistent Wine prefix |
| `VNC_PASSWORD` | empty | Optional direct VNC password, up to 8 characters |
| `VNC_PORT` | `5900` | x11vnc port |
| `NOVNC_PORT` | `6080` | noVNC/websockify port |
## Connecting
Direct VNC clients connect to the server allocation on port `5900`. If `VNC_PASSWORD` is blank, VNC authentication is disabled; protect the allocation with your firewall or proxy.
Browser clients open `http://SERVER:6080/vnc.html` and connect to the displayed VNC endpoint. Use HTTPS or a reverse proxy when exposing noVNC beyond a trusted network.
## Troubleshooting
- **Black screen:** confirm ports are allocated and inspect `/tmp/wine-desktop-xfce.log` and `/tmp/wine-desktop-x11vnc.log` in the container.
- **Application does not start:** run the exact command interactively and check that the executable exists under `/home/container`; the official yolk's `STARTUP` is evaluated by Bash.
- **Prefix problems:** stop the server before changing Wine architecture or winetricks settings. Back up `.wine` before manually repairing it.
- **noVNC loads but cannot connect:** ensure both 6080 and 5900 are reachable from the container network; websockify forwards 6080 to localhost:5900.
- **No audio:** applications must support PulseAudio. The image includes the client and server packages, but application-specific audio configuration may still be required.
## FAQ
**Does this replace the official Wine yolk?** No. It uses it as the base and invokes its original entrypoint after the desktop is ready.
**Does it support Steam server updates and winetricks?** Yes. The inherited entrypoint remains responsible for those features.
**Is the desktop secure by default?** VNC is bound for container access and is intended to be protected by Pterodactyl allocations, firewall rules, or a reverse proxy. Set `VNC_PASSWORD` for direct VNC authentication and do not expose unprotected ports publicly.
**Can I use this with Pelican?** Yes. The Egg uses the modern `PTDL_v2` format and standard Docker image/startup fields.
## License
This project is licensed under the MIT License. The base Wine yolk and its components retain their own licenses.
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env bash
set -Eeuo pipefail
readonly LOG_PREFIX="[wine-desktop]"
DISPLAY="${DISPLAY:-:0}"
export DISPLAY
DISPLAY_WIDTH="${DISPLAY_WIDTH:-1280}"
DISPLAY_HEIGHT="${DISPLAY_HEIGHT:-720}"
DISPLAY_DEPTH="${DISPLAY_DEPTH:-24}"
VNC_PORT="${VNC_PORT:-5900}"
NOVNC_PORT="${NOVNC_PORT:-6080}"
WINEPREFIX="${WINEPREFIX:-/home/container/.wine}"
export DISPLAY DISPLAY_WIDTH DISPLAY_HEIGHT DISPLAY_DEPTH VNC_PORT NOVNC_PORT WINEPREFIX
children=()
cleanup() {
trap - EXIT INT TERM
for pid in "${children[@]:-}"; do
kill "$pid" 2>/dev/null || true
done
}
trap cleanup EXIT INT TERM
log() { echo "${LOG_PREFIX} $*"; }
if ! xdpyinfo -display "$DISPLAY" >/dev/null 2>&1; then
log "Starting Xvfb on ${DISPLAY} (${DISPLAY_WIDTH}x${DISPLAY_HEIGHT}x${DISPLAY_DEPTH})"
Xvfb "$DISPLAY" -screen 0 "${DISPLAY_WIDTH}x${DISPLAY_HEIGHT}x${DISPLAY_DEPTH}" -ac +extension GLX +render -noreset &
children+=("$!")
for _ in {1..50}; do
xdpyinfo -display "$DISPLAY" >/dev/null 2>&1 && break
sleep 0.1
done
fi
if ! xdpyinfo -display "$DISPLAY" >/dev/null 2>&1; then
log "X server did not become ready"
exit 1
fi
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/tmp/runtime-${USER:-container}}"
mkdir -p "$XDG_RUNTIME_DIR"
chmod 0700 "$XDG_RUNTIME_DIR"
if command -v dbus-launch >/dev/null 2>&1; then
eval "$(dbus-launch --sh-syntax)"
children+=("${DBUS_SESSION_BUS_PID:-}")
fi
if command -v pulseaudio >/dev/null 2>&1 && ! pgrep -x pulseaudio >/dev/null 2>&1; then
log "Starting PulseAudio"
pulseaudio --system --daemonize=yes --disallow-exit --exit-idle-time=-1 >/tmp/wine-desktop-pulseaudio.log 2>&1 || \
log "PulseAudio could not start; applications may run without audio"
fi
if ! pgrep -x xfce4-session >/dev/null 2>&1; then
log "Starting XFCE desktop"
startxfce4 >/tmp/wine-desktop-xfce.log 2>&1 &
children+=("$!")
fi
vnc_args=(-display "$DISPLAY" -rfbport "$VNC_PORT" -forever -shared -nopw)
if [[ -n "${VNC_PASSWORD:-}" ]]; then
password_file="${WINEPREFIX}/.vncpasswd"
mkdir -p "$WINEPREFIX"
x11vnc -storepasswd "$VNC_PASSWORD" "$password_file" >/dev/null
chmod 0600 "$password_file"
vnc_args=(-display "$DISPLAY" -rfbport "$VNC_PORT" -forever -shared -rfbauth "$password_file")
fi
log "Starting x11vnc on TCP ${VNC_PORT}"
x11vnc "${vnc_args[@]}" >/tmp/wine-desktop-x11vnc.log 2>&1 &
children+=("$!")
novnc_web="/usr/share/novnc"
[[ -d "$novnc_web" ]] || novnc_web="/usr/share/novnc/utils"
log "Starting noVNC on TCP ${NOVNC_PORT}"
websockify --web="$novnc_web" "${NOVNC_PORT}" "localhost:${VNC_PORT}" >/tmp/wine-desktop-novnc.log 2>&1 &
children+=("$!")
mkdir -p "$WINEPREFIX"
if [[ ! -f "$WINEPREFIX/system.reg" ]]; then
log "Initializing Wine prefix at ${WINEPREFIX}"
wineboot --init >/tmp/wine-desktop-wineboot.log 2>&1 || {
log "Wine prefix initialization failed; see /tmp/wine-desktop-wineboot.log"
exit 1
}
fi
# The inherited command remains the official yolk command, normally:
# /bin/bash /entrypoint.sh. This preserves its update, winetricks, startup,
# and signal behavior while tini remains PID 1.
log "Starting inherited Wine command: $*"
exec "$@"
+78
View File
@@ -0,0 +1,78 @@
{
"_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO",
"meta": {
"version": "PTDL_v2",
"update_url": null
},
"exported_at": "2026-07-30T00:00:00+00:00",
"name": "Wine Desktop",
"author": "[email protected]",
"description": "Runs Windows GUI applications inside Wine using a virtual desktop.",
"features": [],
"docker_images": {
"ghcr.io/lexiandev/wine-desktop:latest": "ghcr.io/lexiandev/wine-desktop:latest",
"ghcr.io/ptero-eggs/yolks:wine_latest": "ghcr.io/ptero-eggs/yolks:wine_latest",
"wine-desktop:latest": "wine-desktop:latest"
},
"file_denylist": [],
"startup": "wine {{SERVER_EXECUTABLE}}",
"config": {
"files": "{}",
"startup": "{\"done\":\"Wine Desktop is ready.\"}",
"logs": "{}",
"stop": "^C"
},
"scripts": {},
"variables": [
{
"name": "Server executable",
"description": "Windows executable path relative to /home/container, for example application.exe or MyApp/MyApp.exe.",
"env_variable": "SERVER_EXECUTABLE",
"default_value": "application.exe",
"user_viewable": true,
"user_editable": true,
"rules": "required|string|max:255",
"field_type": "text"
},
{
"name": "Display width",
"description": "Virtual desktop width in pixels.",
"env_variable": "DISPLAY_WIDTH",
"default_value": "1280",
"user_viewable": true,
"user_editable": true,
"rules": "required|integer|min:640|max:7680",
"field_type": "text"
},
{
"name": "Display height",
"description": "Virtual desktop height in pixels.",
"env_variable": "DISPLAY_HEIGHT",
"default_value": "720",
"user_viewable": true,
"user_editable": true,
"rules": "required|integer|min:480|max:4320",
"field_type": "text"
},
{
"name": "VNC password",
"description": "Optional password for direct VNC access. Leave blank to disable VNC authentication.",
"env_variable": "VNC_PASSWORD",
"default_value": "",
"user_viewable": true,
"user_editable": true,
"rules": "nullable|string|max:8",
"field_type": "text"
},
{
"name": "Wine prefix",
"description": "Persistent Wine prefix directory. Keep the default unless you have a specific reason to change it.",
"env_variable": "WINEPREFIX",
"default_value": "/home/container/.wine",
"user_viewable": true,
"user_editable": true,
"rules": "required|string|max:255",
"field_type": "text"
}
]
}