FROM kasmweb/blender:1.18.0-rolling-daily USER root # WE MUST KEEP THIS ENV BLOCK! # Sets password to match Nginx proxy, disables Wayland, and fixes Blender UI crashing ENV HOME=/home/kasm-user \ STARTUPDIR=/dockerstartup \ VNC_PW=password \ DISPLAY=:1 \ WAYLAND_DISPLAY= \ XDG_RUNTIME_DIR=/tmp/runtime-kasm \ LIBGL_ALWAYS_SOFTWARE=1 \ GALLIUM_DRIVER=llvmpipe \ MESA_GL_VERSION_OVERRIDE=4.5 \ MESA_GLSL_VERSION_OVERRIDE=450 # Install nginx and required GL/Mesa libraries RUN apt-get update && apt-get install -y \ nginx \ libgl1-mesa-dri \ libgl1-mesa-glx \ libglx-mesa0 \ libegl-mesa0 \ libgl1 \ && rm -rf /var/lib/apt/lists/* # 1. Dynamically locate Blender, save its exact path, and move the real binary RUN BLENDER_BIN=$(readlink -f $(which blender)) && \ echo "$BLENDER_BIN" > /tmp/blender_path.txt && \ mv "$BLENDER_BIN" "${BLENDER_BIN}.real" # 2. Write the wrapper in its exact place using the saved path RUN BLENDER_BIN=$(cat /tmp/blender_path.txt) && \ cat > "$BLENDER_BIN" << 'EOF' #!/bin/bash export LIBGL_ALWAYS_SOFTWARE=1 export GALLIUM_DRIVER=llvmpipe export MESA_GL_VERSION_OVERRIDE=4.5 export MESA_GLSL_VERSION_OVERRIDE=450 # Completely disable Wayland so Blender falls back to X11 seamlessly unset WAYLAND_DISPLAY # Preload system libGL so it wins over Blender's bundled copy SYSTEM_GL=$(ldconfig -p | grep libGL.so.1 | grep x86-64 | head -1 | awk '{print $NF}') if [ -n "$SYSTEM_GL" ]; then export LD_PRELOAD="$SYSTEM_GL${LD_PRELOAD:+:$LD_PRELOAD}" fi exec "${0}.real" "$@" EOF # 3. Make the wrapper executable using the saved path RUN BLENDER_BIN=$(cat /tmp/blender_path.txt) && chmod +x "$BLENDER_BIN" # Overwrite Nginx config for Rootless User (User 1000) & proxy to Kasm HTTPS RUN cat > /etc/nginx/nginx.conf << 'EOF' worker_processes 1; daemon off; error_log /tmp/nginx_error.log; pid /tmp/nginx.pid; events { worker_connections 1024; } http { client_body_temp_path /tmp/client_body; proxy_temp_path /tmp/proxy_temp; fastcgi_temp_path /tmp/fastcgi_temp; uwsgi_temp_path /tmp/uwsgi_temp; scgi_temp_path /tmp/scgi_temp; server { listen 7860; location / { # KasmVNC default standalone container serves HTTPS on 6901 proxy_pass https://127.0.0.1:6901; # Websocket support (Required for Kasm stream to work) proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # Auto-inject Basic Auth for Kasm iframe (kasm_user:password) proxy_set_header Authorization "Basic a2FzbV91c2VyOnBhc3N3b3Jk"; proxy_read_timeout 1800s; proxy_send_timeout 1800s; proxy_connect_timeout 60s; proxy_buffering off; } } } EOF # Create a master startup script that starts Nginx, then hands over to Kasm RUN cat > /dockerstartup/hf_startup.sh << 'EOF' #!/bin/bash echo "Starting Nginx reverse proxy on port 7860..." nginx -c /etc/nginx/nginx.conf & echo "Handing off to KasmVNC..." exec /dockerstartup/vnc_startup.sh /dockerstartup/kasm_startup.sh "$@" EOF RUN chmod +x /dockerstartup/hf_startup.sh # Give user 1000 permissions to needed directories RUN mkdir -p /tmp/runtime-kasm && \ chmod 700 /tmp/runtime-kasm && \ chown 1000:0 /tmp/runtime-kasm && \ chown -R 1000:0 /etc/nginx /var/log/nginx /var/lib/nginx /dockerstartup/hf_startup.sh && \ chmod -R 775 /etc/nginx /var/log/nginx /var/lib/nginx # Run as non-root user for Hugging Face Spaces USER 1000 # Expose Hugging Face Space port EXPOSE 7860 # Override the default Kasm ENTRYPOINT to use our wrapper ENTRYPOINT ["/dockerstartup/hf_startup.sh"] CMD ["--wait"]