Drop gevent and the stale backend Dockerfile

The panel runs async_mode='threading' under a plain threaded gunicorn
worker; nothing imports gevent. It was still installed everywhere, and
on Python 3.14 (no wheel yet) its source build is what broke installs
in issue #148. backend/Dockerfile was unreferenced and still launched
the gevent-websocket worker that double-answers the WS handshake; the
run.py hint recommended the same worker.
This commit is contained in:
Juan Denis committed 2026-10-10 13:40:55 -04:00
1 parent 5e4ca2e8a1
commit b739a6b17e
10 files changed
+14 -87

No files matched your search

+2 -4
View File
@@ -44,7 +44,7 @@ RUN npm run build
# Stage 2: Python dependency builder
#
# The compiler toolchain (gcc, *-dev headers) is needed to build wheels for
# gevent / cryptography / lxml on platforms without a prebuilt wheel, but it is
# cryptography / lxml / psutil on platforms without a prebuilt wheel, but it is
# ~280 MB and has no business in a running panel. Building into a self-contained
# venv here lets the runtime stage take the result and leave the toolchain behind.
FROM python:3.11-slim-bookworm AS python-builder
@@ -55,8 +55,6 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
python3-dev \
# Required for gevent
libevent-dev \
# Required for cryptography
libffi-dev \
libssl-dev \
@@ -73,7 +71,7 @@ RUN pip install --no-cache-dir --upgrade pip \
# surfaces as a failed image build, never as a half-installed panel on a host.
#
# 1. Bundled test suites. Third-party packages ship their own pytest trees
# (gevent and passlib are the big ones) that no running panel imports.
# (passlib is the big one) that no running panel imports.
# 2. botocore API models. botocore carries JSON models for 400+ AWS services
# (~15 MB); ServerKit constructs exactly three clients — s3
# (storage_provider_service), route53 (dns_provider_service) and ses
-71
View File
@@ -1,71 +0,0 @@
# Backend Dockerfile for ServerKit
#
# NOTE: A containerized backend CANNOT manage the host (no host Docker daemon,
# nginx, systemctl, or real /var/serverkit/apps). The canonical deployment runs
# the backend on the host via systemd (see install.sh + docker-compose.yml,
# which is frontend-only). This image is only for local/dev or all-in-one demos.
FROM python:3.11-slim-bookworm
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
FLASK_ENV=production \
SERVERKIT_BACKEND_PORT=5000
# Set working directory
WORKDIR /app
# Install system dependencies including ClamAV for security scanning
RUN apt-get update && apt-get install -y --no-install-recommends \
# Build dependencies
gcc \
python3-dev \
libpq-dev \
libffi-dev \
libssl-dev \
libevent-dev \
# ClamAV antivirus
clamav \
clamav-daemon \
clamav-freshclam \
# Utilities
curl \
procps \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean
# Create directories for ServerKit
RUN mkdir -p /etc/serverkit /var/serverkit/apps /app/logs /var/quarantine /app/instance /var/backups/serverkit
# Copy requirements first for caching
COPY requirements.txt .
# Install Python dependencies
RUN pip install --no-cache-dir -r requirements.txt \
&& pip install --no-cache-dir gunicorn gevent gevent-websocket
# Copy application code
COPY . .
# Create non-root user and set permissions. /var/serverkit must be owned by
# appuser too, otherwise the (dev/demo) backend cannot create app directories
# under /var/serverkit/apps and fails with EACCES.
RUN useradd --create-home appuser \
&& chown -R appuser:appuser /app \
&& chown -R appuser:appuser /app/instance \
&& chown -R appuser:appuser /etc/serverkit \
&& chown -R appuser:appuser /var/serverkit \
&& chown -R appuser:appuser /var/quarantine \
&& chown -R appuser:appuser /var/backups/serverkit
USER appuser
# Expose port
EXPOSE 5000
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f "http://localhost:${SERVERKIT_BACKEND_PORT:-5000}/api/v1/system/health" || exit 1
# Run with gunicorn + gevent for WebSocket support
CMD ["sh", "-c", "exec gunicorn -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 -b 0.0.0.0:${SERVERKIT_BACKEND_PORT:-5000} --timeout 120 --access-logfile - --error-logfile - run:app"]
-2
View File
@@ -37,8 +37,6 @@ psutil==5.9.7
# against async_mode='threading' (browser: "Invalid frame header").
gunicorn==23.0.0
simple-websocket==1.1.0
gevent==25.4.2
gevent-websocket==0.10.1
# Environment variables
python-dotenv==1.2.2
+1 -1
View File
@@ -19,7 +19,7 @@ if __name__ == '__main__':
if env == 'production':
print("ERROR: Do not run the development server in production!", file=sys.stderr)
print("Use a production WSGI server like gunicorn instead:", file=sys.stderr)
print(" gunicorn -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 run:app", file=sys.stderr)
print(" gunicorn -w 1 --threads 100 run:app", file=sys.stderr)
sys.exit(1)
# Use SocketIO to run the app (supports WebSocket) - only in development
+1 -1
View File
@@ -1460,7 +1460,7 @@ build_virtualenv() {
else
pip install -r "$INSTALL_DIR/backend/requirements.txt" --quiet
fi
pip install gunicorn gevent gevent-websocket --quiet
pip install gunicorn --quiet
good "Python environment ready."
}
+1 -1
View File
@@ -130,7 +130,7 @@ python3 -m venv --copies "$VENV_BUILD_DIR"
source "$VENV_BUILD_DIR/bin/activate"
pip install --upgrade pip --quiet
pip install -r "$BUILD_DIR/backend/requirements.txt" --quiet
pip install gunicorn gevent gevent-websocket --quiet
pip install gunicorn --quiet
# Make the venv relocatable: rewrite absolute paths in activate scripts and
# shebangs so the venv works at $TARGET_VENV_PATH on the target server.
+1 -1
View File
@@ -368,7 +368,7 @@ sr_deploy() {
( # shellcheck source=/dev/null
source "$SR_VENV/bin/activate"
pip install -q -r "$STAGE_DIR/backend/requirements.txt" \
&& pip install -q gunicorn gevent gevent-websocket ) \
&& pip install -q gunicorn ) \
|| sr_die "pip install failed — previous staging deploy still running."
else
sr_step "Building the Python virtualenv"
+2 -2
View File
@@ -199,8 +199,8 @@ The two modes deliberately make narrower claims than a full install:
- **quick** — shell syntax plus the source-level installer/update/lib/CLI suites.
- **provision** — real repositories through `install.sh`, including supported
Python, venv, pip, SSL, SQLite, ctypes, and a source build/import of the pinned
gevent dependency. Ubuntu 26.04 also installs all backend requirements and
Python, venv, pip, SSL, SQLite, ctypes, and a source build/import of gevent (a
heavy C build that proves the toolchain). Ubuntu 26.04 also installs all backend requirements and
runs `pip check`.
Neither mode proves systemd, nginx, firewall behavior, Docker-in-Docker,
+5 -3
View File
@@ -167,12 +167,14 @@ else
bad "it cannot create a virtualenv"
fi
# Force a source build: a cached/platform wheel would hide missing Python.h
# and compilers, the exact gap behind issue #148. Use the production pin.
native_requirement=$(grep '^gevent==' "$REPO_DIR/backend/requirements.txt")
# and compilers, the exact gap behind issue #148. gevent is no longer a
# panel dependency; it stays here as a heavy C build that proves the
# toolchain works for whatever native dependency comes next.
native_requirement='gevent==25.4.2'
if /tmp/pp-venv/bin/python -m pip install --no-cache-dir --no-binary=gevent \
"$native_requirement" >/tmp/pp-native.log 2>&1 \
&& /tmp/pp-venv/bin/python -c 'import gevent' >/dev/null 2>&1; then
ok "the pinned gevent dependency builds from source and imports"
ok "a native C dependency (gevent) builds from source and imports"
else
bad "native dependency build failed"
tail -40 /tmp/pp-native.log
+1 -1
View File
@@ -696,7 +696,7 @@ rebuild_virtualenv() {
source "$target_dir/bin/activate"
pip install --upgrade pip --quiet
pip install -r "$target_dir/../backend/requirements.txt" --quiet
pip install gunicorn gevent gevent-websocket --quiet
pip install gunicorn --quiet
good "Virtual environment rebuilt at $target_dir"
}