| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
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.
Difficulty: 30/100
Estimated Effort: 2-3 days
Dependencies: None
The only Docker reference is for running CTH tests (external container).
# ============================================
# Stage 1: Builder
# ============================================
FROM node:22-alpine AS builder
WORKDIR /app
# Install dependencies first (cache optimization)
COPY package*.json ./
RUN npm ci
# Copy source
COPY . .
# ============================================
# Stage 2: Production
# ============================================
FROM node:22-alpine AS production
# Install dumb-init for proper signal handling
RUN apk add --no-cache dumb-init
# Create non-root user
RUN addgroup -g 1001 -S jss && \
adduser -S -u 1001 -G jss jss
WORKDIR /app
# Copy only production dependencies and source
COPY --from=builder --chown=jss:jss /app/node_modules ./node_modules
COPY --from=builder --chown=jss:jss /app/package.json ./
COPY --from=builder --chown=jss:jss /app/bin ./bin
COPY --from=builder --chown=jss:jss /app/src ./src
# Create data directory
RUN mkdir -p /data && chown jss:jss /data
# Switch to non-root user
USER jss
# Environment defaults
ENV NODE_ENV=production
ENV JSS_PORT=3000
ENV JSS_HOST=0.0.0.0
ENV JSS_ROOT=/data
# Expose port
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/.well-known/solid || exit 1
# Use dumb-init as PID 1
ENTRYPOINT ["dumb-init", "--"]
# Start server
CMD ["node", "bin/jss.js", "start"]| Feature | Benefit |
|---|---|
| Multi-stage build | ~70% smaller image (node_modules dev deps excluded) |
| Alpine base | Minimal attack surface, ~5x smaller than Debian |
| Non-root user | Security best practice, prevents privilege escalation |
| dumb-init | Proper signal handling, zombie process reaping |
| Layer caching | package.json first = faster rebuilds |
| Health check | Container orchestration support |
Basic (docker-compose.yml)
services:
jss:
image: ghcr.io/javascriptsolidserver/jss:latest
ports:
- "3000:3000"
volumes:
- jss-data:/data
environment:
- JSS_MULTIUSER=true
- JSS_IDP=true
restart: unless-stopped
volumes:
jss-data:Production with SSL (docker-compose.prod.yml)
services:
jss:
image: ghcr.io/javascriptsolidserver/jss:latest
ports:
- "443:3000"
volumes:
- ./data:/data
- ./ssl:/ssl:ro
environment:
- JSS_PORT=3000
- JSS_SSL_KEY=/ssl/key.pem
- JSS_SSL_CERT=/ssl/cert.pem
- JSS_MULTIUSER=true
- JSS_IDP=true
- JSS_CONNEG=true
- JSS_NOTIFICATIONS=true
- JSS_DEFAULT_QUOTA=100MB
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/.well-known/solid"]
interval: 30s
timeout: 10s
retries: 3Full Stack with Reverse Proxy (docker-compose.full.yml)
services:
jss:
image: ghcr.io/javascriptsolidserver/jss:latest
volumes:
- jss-data:/data
environment:
- JSS_MULTIUSER=true
- JSS_IDP=true
- JSS_CONNEG=true
- JSS_NOTIFICATIONS=true
- JSS_ACTIVITYPUB=true
- JSS_NOSTR=true
restart: unless-stopped
networks:
- internal
caddy:
image: caddy:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
depends_on:
- jss
restart: unless-stopped
networks:
- internal
volumes:
jss-data:
caddy-data:
caddy-config:
networks:
internal:Single-User Personal Server
services:
jss:
image: ghcr.io/javascriptsolidserver/jss:latest
ports:
- "3000:3000"
volumes:
- ./data:/data
environment:
- JSS_SINGLE_USER=true
- JSS_SINGLE_USER_NAME=me
- JSS_IDP=true
- JSS_CONNEG=true
- JSS_MASHLIB=true
restart: unless-stopped# Dependencies node_modules # Git .git .gitignore # IDE .vscode .idea *.swp *.swo # Testing test tests *.test.js coverage .nyc_output # Documentation *.md !README.md docs # CI/CD .github .gitlab-ci.yml .travis.yml # Development .env .env.* docker-compose*.yml Dockerfile* # Data (should be mounted as volume) data *.db # Misc .DS_Store Thumbs.db *.log
.github/workflows/docker.yml
name: Docker Build & Push
on:
push:
branches: [main, gh-pages]
tags: ['v*']
pull_request:
branches: [main, gh-pages]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha,prefix=
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/gh-pages' }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64
- name: Generate SBOM
if: github.event_name != 'pull_request'
uses: anchore/sbom-action@v0
with:
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}| Feature | Benefit |
|---|---|
| Multi-platform | linux/amd64 + linux/arm64 (Raspberry Pi, M1/M2 Macs) |
| Semantic versioning | Tags like v1.0.0 create 1.0.0, 1.0, 1 tags |
| Cache | GitHub Actions cache for faster builds |
| SBOM | Software Bill of Materials for security scanning |
| PR builds | Build but don't push on PRs (test Dockerfile) |
Add /.well-known/solid or /health endpoint:
// src/server.js - add health check route
fastify.get('/health', async (request, reply) => {
return {
status: 'healthy',
version: process.env.npm_package_version || 'unknown',
uptime: process.uptime(),
timestamp: new Date().toISOString()
};
});
// Or use existing /.well-known/solid which returns server metadataREADME.md Docker Section
## Docker
### Quick Start
```bash
docker run -d \
--name jss \
-p 3000:3000 \
-v jss-data:/data \
ghcr.io/javascriptsolidserver/jss:latestdocker run -d \
--name jss \
-p 3000:3000 \
-v ./data:/data \
-e JSS_MULTIUSER=true \
-e JSS_IDP=true \
-e JSS_CONNEG=true \
ghcr.io/javascriptsolidserver/jss:latestcurl -O https://raw.githubusercontent.com/JavaScriptSolidServer/JavaScriptSolidServer/main/docker-compose.yml
docker compose up -d| Tag | Description |
|---|---|
| latest | Latest stable release |
| x.y.z | Specific version |
| x.y | Latest patch of minor version |
| x | Latest minor of major version |
| sha-abc123 | Specific commit |
All JSS_* environment variables are supported. See Configuration for full list.
--- ## Industry Comparison | Software | Docker Support | |----------|----------------| | **Community Solid Server** | ✅ Official image `solidproject/community-server` | | **Nextcloud** | ✅ Official image, multiple variants (fpm, apache) | | **Mastodon** | ✅ Official image, comprehensive compose files | | **Gitea/Forgejo** | ✅ Official images, rootless variants | | **Vaultwarden** | ✅ Official image `vaultwarden/server` | | **PocketBase** | ✅ Official image, single binary friendly | | **Matrix Synapse** | ✅ Official image `matrixdotorg/synapse` | | **JSS (current)** | ❌ No Docker support | All major self-hosted software provides official Docker images. This is table stakes. --- ## Image Size Estimates | Stage | Estimated Size | |-------|----------------| | Base `node:22` | ~1.1 GB | | Base `node:22-alpine` | ~180 MB | | With dependencies | ~250 MB | | Multi-stage final | ~80-100 MB | Comparison: - Community Solid Server: ~300 MB - PocketBase: ~50 MB (Go binary) - Nextcloud: ~800 MB (PHP + Apache) --- ## Implementation Checklist - [ ] Create `Dockerfile` with multi-stage build - [ ] Create `.dockerignore` - [ ] Create `docker-compose.yml` (basic) - [ ] Create `docker-compose.prod.yml` (with SSL) - [ ] Create `docker-compose.full.yml` (with Caddy) - [ ] Add health check endpoint (`/health`) - [ ] Create GitHub Actions workflow - [ ] Test on `linux/amd64` - [ ] Test on `linux/arm64` - [ ] Update README with Docker section - [ ] Add example Caddyfile - [ ] First release to ghcr.io --- ## Difficulty Breakdown | Component | Difficulty | |-----------|------------| | Dockerfile | 20/100 | | .dockerignore | 5/100 | | docker-compose files | 15/100 | | GitHub Actions workflow | 25/100 | | Health endpoint | 10/100 | | Documentation | 10/100 | | Multi-arch testing | 20/100 | | **Total** | 30/100 | --- ## Security Considerations 1. **Non-root user** - Container runs as UID 1001, not root 2. **Read-only root filesystem** - Can add `--read-only` flag 3. **No shell** - Consider `FROM scratch` or distroless for minimal attack surface 4. **SBOM generation** - Track dependencies for vulnerability scanning 5. **Signed images** - Consider sigstore/cosign for image signing 6. **Secrets handling** - Document proper secrets management (Docker secrets, env files) --- ## Future Enhancements 1. **Rootless variant** - For environments requiring rootless containers 2. **Distroless variant** - Minimal image without shell 3. **Kubernetes Helm chart** - For K8s deployments 4. **Docker Hub mirror** - Alternative registry 5. **Watchtower compatibility** - Auto-update support --- ## Related Issues - #97 - Presets (Docker compose can use preset env var) - #98 - CLI wizard (`jss generate docker` command) - #95 - Web onboarding (works same in Docker) - #96 - Admin panel (accessible via Docker) --- ## References - [Node.js Docker Best Practices](https://github.com/nodejs/docker-node/blob/main/docs/BestPractices.md) - [Docker Multi-Stage Builds](https://docs.docker.com/build/building/multi-stage/) - [GitHub Container Registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry) - [Docker Build Push Action](https://github.com/docker/build-push-action) - [Community Solid Server Docker](https://hub.docker.com/r/solidproject/community-server)
| Back | FazBrowse Home | New Git URL |
Activity