node, flask, httpx, and static site index, launcher, proxy
Find a file
hermes a07422a525
Some checks failed
CI / lint-launcher (push) Successful in 36s
CI / test-launcher (push) Has been cancelled
CI / lint-frontend (push) Successful in 35s
CI / build-frontend (push) Successful in 34s
CI / debian-package (push) Has been skipped
CI / test (push) Successful in 40s
Deploy zsus / deploy-zsus (push) Failing after 20s
ci: re-run via runner pickup verification
2026-09-26 20:06:56 -07:00
.commits fix #46: GET /api/apps and /api/version return 404 — routes not registering 2026-05-14 16:58:44 -07:00
.forgejo/workflows fix #57: correct zsus path to /home/mark/node_hub and user to mark 2026-06-09 10:44:05 -07:00
.github/workflows fix: use full action URLs for Forgejo compatibility 2026-06-03 20:27:19 -07:00
.sisyphus/notepads/compose-app-support docs: add compose app support session summary 2026-03-01 14:29:30 -08:00
data/.launcher fix: update launcher app port binding to host accessible and reset container state 2026-03-18 18:21:50 -07:00
debian fix: remove nodejs addon from debian rules 2026-03-29 15:07:10 -07:00
deploy gsd: pre-implementation lint/format cleanup 2026-06-07 17:35:37 -07:00
docs fix: resolve lint errors - duplicate keys, inner function declaration, Python syntax error 2026-06-09 09:36:09 -07:00
examples fix(security): add HEALTHCHECK to Dockerfiles, restrict GHA permissions 2026-05-18 13:49:34 -07:00
frontend feat: stop applications on idle after timer to conserve resources 2026-06-07 17:35:37 -07:00
launcher fix: resolve lint errors - duplicate keys, inner function declaration, Python syntax error 2026-06-09 09:36:09 -07:00
scripts fix: resolve lint errors - duplicate keys, inner function declaration, Python syntax error 2026-06-09 09:36:09 -07:00
.dockerignore fix: remove unused node_modules volume (fixes #30) 2026-05-09 07:29:59 -07:00
.env.example feat: stop applications on idle after timer to conserve resources 2026-06-07 17:35:37 -07:00
.env.tail fix: add compose app type support for single-container compose apps 2026-04-13 00:59:55 +00:00
.env.tailscale fix(tailscale): configure API access via Tailscale serve 2026-03-14 17:17:36 -07:00
.gitignore fix #40: Sync rate limit defaults between docs and code 2026-05-15 09:42:37 -07:00
.pre-commit-config.yaml Add basic pre-commit hooks config 2026-06-03 20:45:46 -07:00
.quickfix-run.log gsd: pre-implementation lint/format cleanup 2026-06-07 17:35:37 -07:00
AGENTS.md feat: stop applications on idle after timer to conserve resources 2026-06-07 17:35:37 -07:00
API.md feat: stop applications on idle after timer to conserve resources 2026-06-07 17:35:37 -07:00
bun.lock gsd: pre-implementation lint/format cleanup 2026-06-07 17:35:37 -07:00
CHANGELOG.md feat: stop applications on idle after timer to conserve resources 2026-06-07 17:35:37 -07:00
DEPLOYMENT.md gsd: pre-implementation lint/format cleanup 2026-06-07 17:35:37 -07:00
docker-compose-tail.yml fix #46: GET /api/apps and /api/version return 404 — routes not registering 2026-05-14 16:58:44 -07:00
docker-compose-tail.yml.bak fix: add compose app type support for single-container compose apps 2026-04-13 00:59:55 +00:00
docker-compose.yml gsd: pre-implementation lint/format cleanup 2026-06-07 17:35:37 -07:00
lefthook.yml fix #46: GET /api/apps and /api/version return 404 — routes not registering 2026-05-14 16:58:44 -07:00
package-lock.json fix(deps): upgrade concurrently to v9 to resolve lodash vulnerability 2026-05-18 13:57:20 -07:00
package.json fix(deps): upgrade concurrently to v9 to resolve lodash vulnerability 2026-05-18 13:57:20 -07:00
README.md fix #35: [BUG] CLI interface (launcher/index.js) lacks parity with API - missin 2026-06-07 17:35:37 -07:00
self-update.sh feat: enable tailscale HTTPS serve on port 443 2026-04-18 08:08:26 -07:00
version.json fix #46: GET /api/apps and /api/version return 404 — routes not registering 2026-05-14 16:58:44 -07:00

Node Hub

A desktop-style launcher for managing and running multiple applications in isolated Docker containers. Supports Node.js apps, Docker Compose multi-service apps, Dockerfile builds, pre-built images, and Replit projects.

Table of Contents

Overview

The Node App Launcher provides an itch.io-style interface for Node.js applications, allowing users to upload app bundles and run each one in its own Docker worker container. Each app runs in isolation using a fixed base image with the app code injected as a filesystem payload.

Key Features

  • Isolated Execution: Each app runs in its own Docker container
  • Immutable Base: Single base image for all apps, ensuring consistency
  • Per-App Payloads: Apps are injected as read-only filesystem overlays
  • Persistent Storage: Dedicated volumes for app data
  • Web Interface: Dashboard for managing apps
  • Reverse Proxy Access: Open apps through /proxy/:instanceId without exposing app ports
  • Lifecycle Controls: Stop, view logs, and delete app instances from the UI/API
  • CLI Control: Command-line interface for automation
  • Docker Compose Integration: Multi-service orchestration support
  • Replit Project Support: Deploy Replit projects directly with Nix runtime
  • Staged Deployment: Configure environment variables before starting apps

Architecture

Core Components

  1. Launcher Core - Main orchestration engine
  2. Container Manager - Handles Docker container lifecycle
  3. App Validator - Validates app packages and manifests
  4. Docker Compose Generator - Creates orchestration configurations
  5. Frontend UI - Management interface
  6. CLI Interface - Command-line control

Runtime Model

  • Immutable Worker Image: Single Docker image with Node.js runtime
  • Per-Run Payload: App code injected as read-only filesystem mount
  • Data Volumes: Persistent storage for each app
  • Resource Isolation: Each app runs in its own container namespace
  • Proxy-First Access: Apps are reached through launcher proxy routes, not fixed host ports

Quick Start

Deploy Example Apps

Quickly test Node Hub with these example repositories:

Node.js Hello World:

# Deploy via Git
https://forgejo.tail018ac4.ts.net/Mark/node-hub-hello-world-example.git

Dockerfile Example:

https://forgejo.tail018ac4.ts.net/Mark/node-hub-dockerfile-example.git

Replit Project Example:

https://forgejo.tail018ac4.ts.net/Mark/node-hub-replit-example.git

App Packaging Modes

Mode Use When You Provide Launcher Does
Node App Simple apps with one service ZIP with package.json Runs in Node.js container, auto npm install
Compose App Multi-service apps (web + db) ZIP with docker-compose.yml Uses your compose file with security restrictions
Dockerfile Custom Docker builds ZIP with Dockerfile Builds image and runs container
Pre-built Image Use existing Docker images app.yml with image reference Pulls and runs image directly
Replit Project Replit projects with .replit ZIP with .replit config Uses Nix runtime, handles dependencies

Mode 1: Node Apps (Simple)

For simple single-service Node.js apps, just zip your code and upload. The launcher handles containerization for you.

Package as ZIP:

myapp.zip/
├── app.yml              # Manifest
├── package.json         # npm package file
├── index.js             # Your entry point
└── ...                 # Other app files

app.yml manifest:

id: "my-node-app"
name: "My Node App"
version: "1.0.0"
port: 3000

Optional manifest fields:

id: "my-app"
name: "My App"
version: "1.0.0"
entry:
  command: ["node", "server.js"] # Start command (default: node index.js)
  cwd: "." # Working directory (default: ".")
port: 3000
env:
  NODE_ENV: "production"
  DATABASE_URL: "postgres://..."
persistentDataDir: ".data" # Path for persistent storage

Example apps:

  • Node.js: examples/apps/hello-world/ or repo https://forgejo.tail018ac4.ts.net/Mark/node-hub-hello-world-example.git
  • Compose: examples/apps/compose-example/ or repo https://forgejo.tail018ac4.ts.net/Mark/node-hub-compose-example.git
  • Dockerfile: examples/apps/dockerfile-example/ or repo https://forgejo.tail018ac4.ts.net/Mark/node-hub-dockerfile-example.git
  • Image: examples/apps/image-example/ or repo https://forgejo.tail018ac4.ts.net/Mark/node-hub-image-example.git

Mode 2: Compose Apps (Multi-Service)

For complex apps with multiple services (web server + database, etc.), use the compose mode.

Package as ZIP:

mycompose.zip/
├── app.yml              # Manifest with type: compose
├── docker-compose.yml   # Your compose file
└── ...                 # Optional: other files (not env_file)

app.yml manifest:

id: "my-compose-app"
name: "My Compose App"
version: "1.0.0"
type: "compose"
port: 80
compose:
  proxyService: "web" # The service to proxy via /proxy/:instanceId

docker-compose.yml:

version: "3.8"
services:
  web:
    image: "nginx:alpine"
    depends_on:
      - db
  db:
    image: "postgres:16"
    environment:
      POSTGRES_PASSWORD: "secret"
    volumes:
      - pgdata:/var/lib/postgresql/data
volumes:
  pgdata: {}

Important:

  • The launcher proxies exactly one service (the proxyService) via /proxy/:instanceId/*
  • Only that service publishes a port (loopback-only, random host port)
  • Other services (like db) are internal only - not exposed to the host
  • The payload is mounted at /payload in the proxy service container

Example:

  • See examples/apps/compose-example/

Mode 3: Dockerfile Apps (Custom Builds)

For apps requiring custom Docker builds.

Package as ZIP:

myapp.zip/
├── app.yml              # Manifest with type: dockerfile
├── Dockerfile           # Your Dockerfile
├── package.json         # Optional: npm dependencies
└── ...                 # App files

app.yml manifest:

id: "my-dockerfile-app"
name: "My Dockerfile App"
version: "1.0.0"
type: "dockerfile"
port: 8080
env:
  PORT: "8080"

Example:

  • Repo: https://forgejo.tail018ac4.ts.net/Mark/node-hub-dockerfile-example.git

Mode 4: Pre-built Image Apps (Fastest)

For using existing Docker images without building.

Package as ZIP:

myapp.zip/
└── app.yml              # Only app.yml required!

app.yml manifest:

id: "my-image-app"
name: "My Image App"
version: "1.0.0"
type: "image"
image: "nginx:alpine"
port: 80
env:
  NGINX_HOST: "localhost"

Example:

  • Repo: https://forgejo.tail018ac4.ts.net/Mark/node-hub-image-example.git

Compose Security Limits

For security, the launcher rejects compose files with:

  • ❌ Bind mounts (./, ../, absolute paths in volumes:)
  • ❌ Anonymous volumes (must be named volumes only)
  • ❌ privileged, network_mode: host, pid: host, ipc: host, devices, cap_add, security_opt, extra_hosts
  • ❌ ${VAR} interpolation in compose YAML
  • ❌ external: true networks or volumes
  • ❌ Volume/network driver options (named volumes only)
  • ❌ env_file: in compose (use app.yml env or env editor instead)

Note: build: is now allowed for type: dockerfile apps.


Usage

Web Interface

  1. Open http://localhost:3000
  2. Click "Upload New App" or "Launch from Git Repository"
  3. Select app type from dropdown (Auto/Node/Compose/Dockerfile/Image)
  4. If deploying from Git, the system scans for .env.example files automatically
  5. Configure environment variables (see Environment Management below)
  6. Click deploy - the app validates and launches automatically
  7. Or check "Stage only" to configure environment before starting
  8. Use "Open App" to access the proxied app output
  9. Use "View Logs", "Stop", "Environment", or "Delete" to manage instances

Staged Deployment

For apps that need environment configuration before starting:

  1. Check "Stage only (configure env before starting)" when uploading
  2. App appears in "Staged Apps" section
  3. Click "Configure Env" to add environment variables
  4. Click "Start" when ready to launch

See API.md for staged deployment API endpoints.


Environment Management

Automatic Env File Scanning

When deploying from Git, Node Hub automatically scans for environment files:

  • .env
  • .env.example
  • .env.sample
  • .env.local
  • .env.*

Extracted environment variables are displayed in the UI before deployment, allowing you to:

  • Preview required variables
  • Edit values before launch
  • Upload custom .env files

Environment Variable Editor

Each running app has an "Environment" button in the UI:

  • View current environment variables
  • Edit values inline
  • Add new variables
  • Delete existing variables
  • Changes trigger automatic container restart
  • Persists across app restarts

API Endpoints:

# Get current env vars
GET /api/apps/:instanceId/env

# Update env vars (restarts container)
POST /api/apps/:instanceId/env
Content-Type: application/json
{"KEY": "value", "PORT": "3000"}

# Preview env files in Git repo
POST /api/apps/preview-env
Content-Type: application/json
{"repoUrl": "https://git.example.com/user/repo.git", "ref": "main"}

Authentication

Node Hub supports multiple authentication methods:

Session-Based (Web UI)

Users log in via the web interface with username/password. Sessions are stored in HTTP-only cookies with 24-hour expiration.

# First-time setup
curl -X POST http://localhost:8000/api/auth/setup \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "password": "secure-password"}'

# Login
curl -X POST http://localhost:8000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "password": "secure-password"}' \
  -c cookies.txt

API Token (Legacy)

Set LAUNCHER_API_TOKEN for simple API access:

curl -H "Authorization: Bearer YOUR_LAUNCHER_API_TOKEN" \
  http://localhost:8000/api/apps

Users can create PATs for API access without using their password:

# Create PAT (via web UI or API when logged in)
# Use PAT in API calls
curl -H "Authorization: Bearer YOUR_PAT" \
  http://localhost:8000/api/apps

Authentication Flow

  1. First setup: Create admin user via /api/auth/setup (one-time)
  2. Web UI: Login with username/password, receive session cookie
  3. API access: Use PAT or legacy API token
  4. Health check: /health and /api/version are public (no auth)

Security Notes

  • Passwords hashed with PBKDF2 (100k iterations)
  • Session cookies are HTTP-only and secure (when TLS enabled)
  • PATs are hashed before storage (SHA-256)
  • Rate limiting applies to all authenticated endpoints

HTTP API

# List all running instances
curl http://localhost:8000/api/apps

# Upload and launch a Node app (ZIP)
curl -X POST http://localhost:8000/api/apps \
  -F "app=@myapp.zip"

# Launch from git repository
curl -X POST http://localhost:8000/api/apps/repo \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{
    "repoUrl": "https://git.example.com/user/repo.git",
    "ref": "main",
    "subdir": "path/to/app"
  }'

# Pull latest (update repo-launched app)
curl -X POST http://localhost:8000/api/apps/instance-id/pull \
  -H "Authorization: Bearer YOUR_TOKEN"

# Get instance status
curl http://localhost:8000/api/apps/instance-id/status

# Get instance logs
curl http://localhost:8000/api/apps/instance-id/logs

# Stop instance
curl -X POST http://localhost:8000/api/apps/instance-id/stop

# Delete instance
curl -X DELETE http://localhost:8000/api/apps/instance-id

# Access proxied app
curl http://localhost:8000/proxy/instance-id/

CLI Interface

# Launch an app
node launcher/index.js launch /path/to/app.zip

# Stop a running app
node launcher/index.js stop instance-id

# Restart an app
node launcher/index.js restart instance-id

# Check app status
node launcher/index.js status instance-id

# View app logs
node launcher/index.js logs instance-id

# List all apps
node launcher/index.js list

# Delete an app
node launcher/index.js delete instance-id

# Pull latest git changes for an app
node launcher/index.js pull instance-id

# Stage an app for launch
node launcher/index.js stage /path/to/app.zip instance-id

# Start a staged app
node launcher/index.js start instance-id

# Get environment variables
node launcher/index.js env get instance-id

# Update environment variables
node launcher/index.js env set instance-id KEY=VALUE [KEY=VALUE ...]

# Deploy from git repository
node launcher/index.js deploy https://github.com/user/repo.git instance-id

Forgejo/Gitea Integration

As a Deployment Target

The launcher is a deployment target for Forgejo Actions. After your CI tests pass, Forgejo can trigger a deployment to the launcher.

This is NOT a Forgejo runner (it doesn't run your CI tests). It's a destination where deployments go after testing.

Setup

  1. Configure API token in the launcher:

    LAUNCHER_API_TOKEN=your-secret-token
    
  2. Add secrets in your Forgejo repository:

    • LAUNCHER_URL - e.g., https://launcher.yourdomain.com
    • LAUNCHER_TOKEN - the token from step 1
  3. Add workflow to .forgejo/workflows/deploy.yml:

name: Deploy

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm install && npm test

  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Deploy to Launcher
        env:
          LAUNCHER_URL: ${{ secrets.LAUNCHER_URL }}
          LAUNCHER_TOKEN: ${{ secrets.LAUNCHER_TOKEN }}
        run: |
          RESPONSE=$(curl -s -X POST "$LAUNCHER_URL/api/apps/repo" \
            -H "Authorization: Bearer $LAUNCHER_TOKEN" \
            -H "Content-Type: application/json" \
            -d "{
              \"repoUrl\": \"${{ github.server_url }}/${{ github.repository }}\",
              \"ref\": \"${{ github.ref_name }}\"
            }")

          JOB_ID=$(echo $RESPONSE | jq -r '.jobId')
          echo "Job ID: $JOB_ID"

          # Poll for completion
          for i in $(seq 1 60); do
            sleep 2
            STATUS=$(curl -s "$LAUNCHER_URL/api/jobs/$JOB_ID" | jq -r '.status')
            echo "Status: $STATUS"
            if [ "$STATUS" = "succeeded" ]; then
              echo "Deployed!"
              exit 0
            elif [ "$STATUS" = "failed" ]; then
              echo "Failed!"
              exit 1
            fi
          done
          echo "Timeout"
          exit 1

Using Forgejo Docker Registry

You can also use Forgejo as a Docker registry for your images:

  1. Build and push in your Forgejo workflow:

    - name: Build and push
      run: |
        docker build -t ${{ secrets.FORGEJO_URL }}/${{ github.repository }}/myapp:latest .
        echo ${{ secrets.FORGEJO_TOKEN }} | docker login ${{ secrets.FORGEJO_URL }} -u ${{ github.actor }} --password-stdin
        docker push ${{ secrets.FORGEJO_URL }}/${{ github.repository }}/myapp:latest
    
  2. Reference in compose app:

    services:
      web:
        image: "https://git.yourdomain.com/youruser/yourrepo/myapp:latest"
    

Configuration

Launcher Configuration

The launcher can be configured via environment variables:

# Port (default: 8000)
LAUNCHER_PORT=8000

# Directories
LAUNCHER_PAYLOAD_DIR=/path/to/payloads
LAUNCHER_COMPOSE_DIR=/path/to/compose
LAUNCHER_STATE_FILE=/path/to/state/containers.json

# Docker
DOCKER_HOST=unix:///var/run/docker.sock
LAUNCHER_BASE_IMAGE=node:20-bookworm-slim
LAUNCHER_DOCKER_TIMEOUT_MS=15000

# Security
LAUNCHER_API_TOKEN=change-me
LAUNCHER_REPO_HOST_ALLOWLIST=github.com,git.example.com
LAUNCHER_ALLOW_INSECURE_HTTP=false
LAUNCHER_GIT_TIMEOUT_MS=60000

# Job queue
LAUNCHER_JOB_CONCURRENCY=1
LAUNCHER_JOB_MAX=500

# Zip extraction limits
LAUNCHER_ZIP_MAX_ENTRIES=2000
LAUNCHER_ZIP_MAX_UNCOMPRESSED_BYTES=209715200
LAUNCHER_ZIP_MAX_FILE_BYTES=52428800

# Forgejo integration (optional)
FORGEJO_URL=https://git.example.com
FORGEJO_TOKEN=your-forgejo-api-token

TLS / SSL

For TLS/SSL configuration, see the Deployment Guide. It covers:

  • Reverse proxy setup (recommended for production)
  • Native TLS for simple deployments
  • All TLS environment variables
  • WebSocket proxy requirements

App Configuration

Each app can specify its own configuration in app.yml:

id: "my-app-id" # Unique per app (required)
name: "My App" # Display name (required)
version: "1.0.0" # App version (required)
type: "compose" # "compose" for multi-service, omit for Node (optional)
port: 3000 # Port the app listens on (required)
entry:
  command: ["node", "index.js"] # Command to start (Node mode)
  cwd: "." # Working directory
env:
  NODE_ENV: "production" # Environment variables
persistentDataDir: ".data" # Path for persistent storage
compose:
  proxyService: "web" # Service to proxy (compose mode)

Docker Integration

Container Security

  • Non-root Execution: Containers run as non-root user
  • Capability Dropping: Minimal capabilities required
  • Read-only Payloads: App code mounted as read-only
  • Isolated Networks: Each app in its own network namespace
  • Proxy-Only Ports: Apps only accessible via proxy, not directly

How the Proxy Works

  1. Each app container publishes its port to a random host port bound to localhost only (127.0.0.1::PORT)
  2. The launcher proxy routes requests from /proxy/:instanceId/* to that random port
  3. This means:
    • Apps are not directly accessible from the network
    • You can run multiple apps without port conflicts
    • The launcher handles routing

WebSocket Support

The proxy supports WebSocket upgrade connections. Apps can use WebSockets for real-time features:

// Client connects via the proxy
const ws = new WebSocket("ws://localhost:8000/proxy/my-app/ws");

When using WebSockets behind a reverse proxy (like Nginx or Caddy), ensure the proxy supports the Connection: Upgrade header.

Automatic Header Rewriting

The proxy automatically rewrites headers to ensure apps work correctly behind the subpath:

  • Location headers: Redirects like Location: /dashboard become Location: /proxy/:instanceId/dashboard
  • Set-Cookie headers: Cookies without a Path get Path=/proxy/:instanceId added
  • Absolute URLs: Preserved unchanged (e.g., https://external.com/page)

This allows apps to issue redirects and set cookies without knowing they're behind a proxy.

Security Headers

The proxy strips sensitive headers before forwarding:

  • Authorization
  • Cookie
  • Any headers listed in the Connection header (hop-by-hop headers)

Terminology

  • Reverse proxy: the launcher accepts inbound requests and forwards them to a launched app.
  • Port forwarding: direct host-port exposure of an app port; the launcher avoids this for app ingress by using loopback-only publishing plus /proxy/:instanceId/*.
  • Forward proxy: a client-side proxy for outbound traffic; not used by this project.

Development

Project Structure

node-app-launcher/
├── launcher/              # Core launcher code
│   ├── core/             # Main orchestration
│   ├── container/        # Docker container management
│   ├── validation/       # App validation
│   ├── config/          # Configuration
│   └── test/            # Tests
├── frontend/             # Web interface
├── examples/             # Example apps
│   └── apps/
│       ├── smoke-test-app/    # Simple Node app
│       ├── hello-world/        # Another Node example
│       └── compose-example/    # Multi-service compose
├── docker-compose.yml    # Development environment
└── README.md

Running Tests

cd launcher
npm test

Deployment

Production Deployment

# Build Docker images
docker-compose build

# Deploy
docker-compose up -d

# Access at http://localhost:3000

Tailscale Sidecar Deployment

For private networking with Tailscale, use docker-compose-tail.yml which runs the launcher UI and API inside the Tailscale container's network namespace:

# Copy and configure environment
cp .env.example .env
# Edit .env and set:
# - LAUNCHER_API_TOKEN
# - LAUNCHER_UI_PORT=80
# - TAILSCALE_AUTHKEY

# Deploy with Tailscale sidecar
docker-compose -f docker-compose-tail.yml up -d --build

# Verify Tailscale connectivity
docker exec nodehub-tailscale tailscale status

# Access via Tailscale MagicDNS
curl http://nodehub.tail018ac4.ts.net:${LAUNCHER_UI_PORT:-80}

Key features of Tailscale sidecar mode:

  • No host port publishing required
  • Services use network_mode: "service:tailscale" to share Tailscale's network namespace
  • Access via Tailscale MagicDNS (e.g., nodehub.tail018ac4.ts.net)
  • UI port configurable via LAUNCHER_UI_PORT environment variable
  • CORS origins include Tailscale domain automatically

Running the Launcher in Docker

The launcher itself can run in a container, but it needs access to Docker:

docker run -d \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -p 8000:8000 \
  -e LAUNCHER_API_TOKEN=secret \
  -e LAUNCHER_PROXY_TARGET_HOST=host.docker.internal \
  node-app-launcher

If your Docker does not support host.docker.internal, use a host-gateway mapping (Docker 20.10+):

docker run -d \
  --add-host=host.docker.internal:host-gateway \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -p 8000:8000 \
  -e LAUNCHER_API_TOKEN=secret \
  -e LAUNCHER_PROXY_TARGET_HOST=host.docker.internal \
  node-app-launcher

Troubleshooting

Common Issues

  1. Docker not running: Ensure Docker daemon is started
  2. Permission denied: Check Docker socket permissions
  3. Proxy errors: Verify app is running and reachable through /proxy/:instanceId
  4. Invalid app package: Check app structure and manifest

Rollout Verification Checklist

# 1) Launcher API is healthy
curl -fsS http://127.0.0.1:8000/health

# 2) Run the proxy E2E smoke (requires Docker)
./scripts/proxy-e2e-smoke.sh

If the proxy smoke fails, inspect the API logs and the running app container:

tail -n +1 .launcher/api-proxy-smoke.log
docker ps --filter 'name=launcher-app-'

Debug Mode

DEBUG=launcher:* node launcher/index.js launch app.zip

View Logs

# Via API
curl http://localhost:8000/api/apps/instance-id/logs

# Or directly
docker logs launcher-app-instance-id

Recent Changes

v2.0.0 - New Features

  • New App Types: Added support for Dockerfile and pre-built image deployments
  • App Type Selection: Dropdown in UI to select Node/Compose/Dockerfile/Image or Auto-detect
  • Environment Variable Editor: Edit env vars without redeploying (persists across restarts)
  • Env File Scanning: Automatic discovery of .env.example, .env.* files from Git repos
  • Env File Upload: Upload .env files during deployment
  • Payload Mount Fix: Fixed bug where payload directory wasn't properly mounted causing "Cannot find module" errors
  • Tailscale API Access: API now accessible via Tailscale MagicDNS domain
  • Example Repos: Created standalone example repos on Forgejo for all app types

v1.x - Original Features

  • Node.js and Docker Compose app support
  • Git repository deployment
  • Web UI and REST API
  • Container lifecycle management
  • Reverse proxy access
  • Forgejo integration

License

MIT License - see LICENSE file for details.