Title: Feature: Docker Support - Dockerfile, Compose, and Container Registry · Issue #99 · JavaScriptSolidServer/JavaScriptSolidServer · GitHub
Open Graph Title: Feature: Docker Support - Dockerfile, Compose, and Container Registry · Issue #99 · JavaScriptSolidServer/JavaScriptSolidServer
X Title: Feature: Docker Support - Dockerfile, Compose, and Container Registry · Issue #99 · JavaScriptSolidServer/JavaScriptSolidServer
Description: Summary Add first-class Docker support to JavaScriptSolidServer, enabling users to deploy with a single docker run command. This is a P0 priority item as Docker is the expected deployment method for modern self-hosted software. Difficult...
Open Graph Description: Summary Add first-class Docker support to JavaScriptSolidServer, enabling users to deploy with a single docker run command. This is a P0 priority item as Docker is the expected deployment method fo...
X Description: Summary Add first-class Docker support to JavaScriptSolidServer, enabling users to deploy with a single docker run command. This is a P0 priority item as Docker is the expected deployment method fo...
Opengraph URL: https://github.com/JavaScriptSolidServer/JavaScriptSolidServer/issues/99
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Feature: Docker Support - Dockerfile, Compose, and Container Registry","articleBody":"## Summary\n\nAdd first-class Docker support to JavaScriptSolidServer, enabling users to deploy with a single `docker run` command. This is a **P0 priority** item as Docker is the expected deployment method for modern self-hosted software.\n\n**Difficulty**: 30/100 \n**Estimated Effort**: 2-3 days \n**Dependencies**: None\n\n---\n\n## Current State\n\n- ❌ No `Dockerfile`\n- ❌ No `docker-compose.yml`\n- ❌ No `.dockerignore`\n- ❌ No container registry images\n- ❌ No health check endpoint\n- ❌ No Docker documentation\n\nThe only Docker reference is for running CTH tests (external container).\n\n---\n\n## Proposed Implementation\n\n### 1. Dockerfile (Multi-Stage Build)\n\n```dockerfile\n# ============================================\n# Stage 1: Builder\n# ============================================\nFROM node:22-alpine AS builder\n\nWORKDIR /app\n\n# Install dependencies first (cache optimization)\nCOPY package*.json ./\nRUN npm ci\n\n# Copy source\nCOPY . .\n\n# ============================================\n# Stage 2: Production\n# ============================================\nFROM node:22-alpine AS production\n\n# Install dumb-init for proper signal handling\nRUN apk add --no-cache dumb-init\n\n# Create non-root user\nRUN addgroup -g 1001 -S jss \u0026\u0026 \\\n adduser -S -u 1001 -G jss jss\n\nWORKDIR /app\n\n# Copy only production dependencies and source\nCOPY --from=builder --chown=jss:jss /app/node_modules ./node_modules\nCOPY --from=builder --chown=jss:jss /app/package.json ./\nCOPY --from=builder --chown=jss:jss /app/bin ./bin\nCOPY --from=builder --chown=jss:jss /app/src ./src\n\n# Create data directory\nRUN mkdir -p /data \u0026\u0026 chown jss:jss /data\n\n# Switch to non-root user\nUSER jss\n\n# Environment defaults\nENV NODE_ENV=production\nENV JSS_PORT=3000\nENV JSS_HOST=0.0.0.0\nENV JSS_ROOT=/data\n\n# Expose port\nEXPOSE 3000\n\n# Health check\nHEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\\n CMD wget --no-verbose --tries=1 --spider http://localhost:3000/.well-known/solid || exit 1\n\n# Use dumb-init as PID 1\nENTRYPOINT [\"dumb-init\", \"--\"]\n\n# Start server\nCMD [\"node\", \"bin/jss.js\", \"start\"]\n```\n\n### Key Dockerfile Features\n\n| Feature | Benefit |\n|---------|---------|\n| Multi-stage build | ~70% smaller image (node_modules dev deps excluded) |\n| Alpine base | Minimal attack surface, ~5x smaller than Debian |\n| Non-root user | Security best practice, prevents privilege escalation |\n| dumb-init | Proper signal handling, zombie process reaping |\n| Layer caching | package.json first = faster rebuilds |\n| Health check | Container orchestration support |\n\n---\n\n### 2. Docker Compose Examples\n\n**Basic (`docker-compose.yml`)**\n```yaml\nservices:\n jss:\n image: ghcr.io/javascriptsolidserver/jss:latest\n ports:\n - \"3000:3000\"\n volumes:\n - jss-data:/data\n environment:\n - JSS_MULTIUSER=true\n - JSS_IDP=true\n restart: unless-stopped\n\nvolumes:\n jss-data:\n```\n\n**Production with SSL (`docker-compose.prod.yml`)**\n```yaml\nservices:\n jss:\n image: ghcr.io/javascriptsolidserver/jss:latest\n ports:\n - \"443:3000\"\n volumes:\n - ./data:/data\n - ./ssl:/ssl:ro\n environment:\n - JSS_PORT=3000\n - JSS_SSL_KEY=/ssl/key.pem\n - JSS_SSL_CERT=/ssl/cert.pem\n - JSS_MULTIUSER=true\n - JSS_IDP=true\n - JSS_CONNEG=true\n - JSS_NOTIFICATIONS=true\n - JSS_DEFAULT_QUOTA=100MB\n restart: unless-stopped\n healthcheck:\n test: [\"CMD\", \"wget\", \"--spider\", \"-q\", \"http://localhost:3000/.well-known/solid\"]\n interval: 30s\n timeout: 10s\n retries: 3\n```\n\n**Full Stack with Reverse Proxy (`docker-compose.full.yml`)**\n```yaml\nservices:\n jss:\n image: ghcr.io/javascriptsolidserver/jss:latest\n volumes:\n - jss-data:/data\n environment:\n - JSS_MULTIUSER=true\n - JSS_IDP=true\n - JSS_CONNEG=true\n - JSS_NOTIFICATIONS=true\n - JSS_ACTIVITYPUB=true\n - JSS_NOSTR=true\n restart: unless-stopped\n networks:\n - internal\n\n caddy:\n image: caddy:alpine\n ports:\n - \"80:80\"\n - \"443:443\"\n volumes:\n - ./Caddyfile:/etc/caddy/Caddyfile:ro\n - caddy-data:/data\n - caddy-config:/config\n depends_on:\n - jss\n restart: unless-stopped\n networks:\n - internal\n\nvolumes:\n jss-data:\n caddy-data:\n caddy-config:\n\nnetworks:\n internal:\n```\n\n**Single-User Personal Server**\n```yaml\nservices:\n jss:\n image: ghcr.io/javascriptsolidserver/jss:latest\n ports:\n - \"3000:3000\"\n volumes:\n - ./data:/data\n environment:\n - JSS_SINGLE_USER=true\n - JSS_SINGLE_USER_NAME=me\n - JSS_IDP=true\n - JSS_CONNEG=true\n - JSS_MASHLIB=true\n restart: unless-stopped\n```\n\n---\n\n### 3. `.dockerignore`\n\n```\n# Dependencies\nnode_modules\n\n# Git\n.git\n.gitignore\n\n# IDE\n.vscode\n.idea\n*.swp\n*.swo\n\n# Testing\ntest\ntests\n*.test.js\ncoverage\n.nyc_output\n\n# Documentation\n*.md\n!README.md\ndocs\n\n# CI/CD\n.github\n.gitlab-ci.yml\n.travis.yml\n\n# Development\n.env\n.env.*\ndocker-compose*.yml\nDockerfile*\n\n# Data (should be mounted as volume)\ndata\n*.db\n\n# Misc\n.DS_Store\nThumbs.db\n*.log\n```\n\n---\n\n### 4. GitHub Actions Workflow\n\n**`.github/workflows/docker.yml`**\n```yaml\nname: Docker Build \u0026 Push\n\non:\n push:\n branches: [main, gh-pages]\n tags: ['v*']\n pull_request:\n branches: [main, gh-pages]\n\nenv:\n REGISTRY: ghcr.io\n IMAGE_NAME: ${{ github.repository }}\n\njobs:\n build:\n runs-on: ubuntu-latest\n permissions:\n contents: read\n packages: write\n\n steps:\n - name: Checkout repository\n uses: actions/checkout@v4\n\n - name: Set up Docker Buildx\n uses: docker/setup-buildx-action@v3\n\n - name: Log in to Container Registry\n if: github.event_name != 'pull_request'\n uses: docker/login-action@v3\n with:\n registry: ${{ env.REGISTRY }}\n username: ${{ github.actor }}\n password: ${{ secrets.GITHUB_TOKEN }}\n\n - name: Extract metadata\n id: meta\n uses: docker/metadata-action@v5\n with:\n images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}\n tags: |\n type=ref,event=branch\n type=ref,event=pr\n type=semver,pattern={{version}}\n type=semver,pattern={{major}}.{{minor}}\n type=semver,pattern={{major}}\n type=sha,prefix=\n type=raw,value=latest,enable=${{ github.ref == 'refs/heads/gh-pages' }}\n\n - name: Build and push\n uses: docker/build-push-action@v5\n with:\n context: .\n push: ${{ github.event_name != 'pull_request' }}\n tags: ${{ steps.meta.outputs.tags }}\n labels: ${{ steps.meta.outputs.labels }}\n cache-from: type=gha\n cache-to: type=gha,mode=max\n platforms: linux/amd64,linux/arm64\n\n - name: Generate SBOM\n if: github.event_name != 'pull_request'\n uses: anchore/sbom-action@v0\n with:\n image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}\n```\n\n### Workflow Features\n\n| Feature | Benefit |\n|---------|---------|\n| Multi-platform | `linux/amd64` + `linux/arm64` (Raspberry Pi, M1/M2 Macs) |\n| Semantic versioning | Tags like `v1.0.0` create `1.0.0`, `1.0`, `1` tags |\n| Cache | GitHub Actions cache for faster builds |\n| SBOM | Software Bill of Materials for security scanning |\n| PR builds | Build but don't push on PRs (test Dockerfile) |\n\n---\n\n### 5. Health Check Endpoint\n\nAdd `/.well-known/solid` or `/health` endpoint:\n\n```javascript\n// src/server.js - add health check route\n\nfastify.get('/health', async (request, reply) =\u003e {\n return {\n status: 'healthy',\n version: process.env.npm_package_version || 'unknown',\n uptime: process.uptime(),\n timestamp: new Date().toISOString()\n };\n});\n\n// Or use existing /.well-known/solid which returns server metadata\n```\n\n---\n\n### 6. Documentation Updates\n\n**README.md Docker Section**\n```markdown\n## Docker\n\n### Quick Start\n\n```bash\ndocker run -d \\\n --name jss \\\n -p 3000:3000 \\\n -v jss-data:/data \\\n ghcr.io/javascriptsolidserver/jss:latest\n```\n\n### With Configuration\n\n```bash\ndocker run -d \\\n --name jss \\\n -p 3000:3000 \\\n -v ./data:/data \\\n -e JSS_MULTIUSER=true \\\n -e JSS_IDP=true \\\n -e JSS_CONNEG=true \\\n ghcr.io/javascriptsolidserver/jss:latest\n```\n\n### Docker Compose\n\n```bash\ncurl -O https://raw.githubusercontent.com/JavaScriptSolidServer/JavaScriptSolidServer/main/docker-compose.yml\ndocker compose up -d\n```\n\n### Available Tags\n\n| Tag | Description |\n|-----|-------------|\n| `latest` | Latest stable release |\n| `x.y.z` | Specific version |\n| `x.y` | Latest patch of minor version |\n| `x` | Latest minor of major version |\n| `sha-abc123` | Specific commit |\n\n### Environment Variables\n\nAll `JSS_*` environment variables are supported. See [Configuration](#configuration) for full list.\n```\n\n---\n\n## Industry Comparison\n\n| Software | Docker Support |\n|----------|----------------|\n| **Community Solid Server** | ✅ Official image `solidproject/community-server` |\n| **Nextcloud** | ✅ Official image, multiple variants (fpm, apache) |\n| **Mastodon** | ✅ Official image, comprehensive compose files |\n| **Gitea/Forgejo** | ✅ Official images, rootless variants |\n| **Vaultwarden** | ✅ Official image `vaultwarden/server` |\n| **PocketBase** | ✅ Official image, single binary friendly |\n| **Matrix Synapse** | ✅ Official image `matrixdotorg/synapse` |\n| **JSS (current)** | ❌ No Docker support |\n\nAll major self-hosted software provides official Docker images. This is table stakes.\n\n---\n\n## Image Size Estimates\n\n| Stage | Estimated Size |\n|-------|----------------|\n| Base `node:22` | ~1.1 GB |\n| Base `node:22-alpine` | ~180 MB |\n| With dependencies | ~250 MB |\n| Multi-stage final | ~80-100 MB |\n\nComparison:\n- Community Solid Server: ~300 MB\n- PocketBase: ~50 MB (Go binary)\n- Nextcloud: ~800 MB (PHP + Apache)\n\n---\n\n## Implementation Checklist\n\n- [ ] Create `Dockerfile` with multi-stage build\n- [ ] Create `.dockerignore`\n- [ ] Create `docker-compose.yml` (basic)\n- [ ] Create `docker-compose.prod.yml` (with SSL)\n- [ ] Create `docker-compose.full.yml` (with Caddy)\n- [ ] Add health check endpoint (`/health`)\n- [ ] Create GitHub Actions workflow\n- [ ] Test on `linux/amd64`\n- [ ] Test on `linux/arm64`\n- [ ] Update README with Docker section\n- [ ] Add example Caddyfile\n- [ ] First release to ghcr.io\n\n---\n\n## Difficulty Breakdown\n\n| Component | Difficulty |\n|-----------|------------|\n| Dockerfile | 20/100 |\n| .dockerignore | 5/100 |\n| docker-compose files | 15/100 |\n| GitHub Actions workflow | 25/100 |\n| Health endpoint | 10/100 |\n| Documentation | 10/100 |\n| Multi-arch testing | 20/100 |\n| **Total** | 30/100 |\n\n---\n\n## Security Considerations\n\n1. **Non-root user** - Container runs as UID 1001, not root\n2. **Read-only root filesystem** - Can add `--read-only` flag\n3. **No shell** - Consider `FROM scratch` or distroless for minimal attack surface\n4. **SBOM generation** - Track dependencies for vulnerability scanning\n5. **Signed images** - Consider sigstore/cosign for image signing\n6. **Secrets handling** - Document proper secrets management (Docker secrets, env files)\n\n---\n\n## Future Enhancements\n\n1. **Rootless variant** - For environments requiring rootless containers\n2. **Distroless variant** - Minimal image without shell\n3. **Kubernetes Helm chart** - For K8s deployments\n4. **Docker Hub mirror** - Alternative registry\n5. **Watchtower compatibility** - Auto-update support\n\n---\n\n## Related Issues\n\n- #97 - Presets (Docker compose can use preset env var)\n- #98 - CLI wizard (`jss generate docker` command)\n- #95 - Web onboarding (works same in Docker)\n- #96 - Admin panel (accessible via Docker)\n\n---\n\n## References\n\n- [Node.js Docker Best Practices](https://github.com/nodejs/docker-node/blob/main/docs/BestPractices.md)\n- [Docker Multi-Stage Builds](https://docs.docker.com/build/building/multi-stage/)\n- [GitHub Container Registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry)\n- [Docker Build Push Action](https://github.com/docker/build-push-action)\n- [Community Solid Server Docker](https://hub.docker.com/r/solidproject/community-server)","author":{"url":"https://github.com/melvincarvalho","@type":"Person","name":"melvincarvalho"},"datePublished":"2026-01-19T13:18:08.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":0},"url":"https://github.com/99/JavaScriptSolidServer/issues/99"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:a4d60e95-cdac-9a12-27e8-e8f806fbada7 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | D416:10BDCB:454031:5FD0E3:69774E12 |
| html-safe-nonce | f085f01931b990d8bb33b32517fe612513983224af277a11cdfccd375c1a1ba1 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJENDE2OjEwQkRDQjo0NTQwMzE6NUZEMEUzOjY5Nzc0RTEyIiwidmlzaXRvcl9pZCI6IjIyMTAxOTgxNDgwNDUyOTUxMjIiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | 29b2043e9eab6bc395fdbf3a392a8964d3689aedd133847df9031ade395b7463 |
| hovercard-subject-tag | issue:3829698876 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/JavaScriptSolidServer/JavaScriptSolidServer/99/issue_layout |
| twitter:image | https://opengraph.githubassets.com/0a695cac4bfdd18a6210b5d8440914500efa137823d02f09b1a549666a64edcf/JavaScriptSolidServer/JavaScriptSolidServer/issues/99 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/0a695cac4bfdd18a6210b5d8440914500efa137823d02f09b1a549666a64edcf/JavaScriptSolidServer/JavaScriptSolidServer/issues/99 |
| og:image:alt | Summary Add first-class Docker support to JavaScriptSolidServer, enabling users to deploy with a single docker run command. This is a P0 priority item as Docker is the expected deployment method fo... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | melvincarvalho |
| hostname | github.com |
| expected-hostname | github.com |
| None | 3310064f35a62c06a4024ba37f41c06836f39376a095c2dfd2c4b693c34965be |
| turbo-cache-control | no-preview |
| go-import | github.com/JavaScriptSolidServer/JavaScriptSolidServer git https://github.com/JavaScriptSolidServer/JavaScriptSolidServer.git |
| octolytics-dimension-user_id | 205442424 |
| octolytics-dimension-user_login | JavaScriptSolidServer |
| octolytics-dimension-repository_id | 958025407 |
| octolytics-dimension-repository_nwo | JavaScriptSolidServer/JavaScriptSolidServer |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 958025407 |
| octolytics-dimension-repository_network_root_nwo | JavaScriptSolidServer/JavaScriptSolidServer |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 67d5f8d1d53c3cc4f49fc3bb8029933c3dc219e6 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width