Skip to content

Feat--Add-Dockerfile-and-update#11

Closed
theepicsaxguy wants to merge 16 commits intoroot-fr:mainfrom
theepicsaxguy:feat--Add-Dockerfile-and-update
Closed

Feat--Add-Dockerfile-and-update#11
theepicsaxguy wants to merge 16 commits intoroot-fr:mainfrom
theepicsaxguy:feat--Add-Dockerfile-and-update

Conversation

@theepicsaxguy
Copy link

@theepicsaxguy theepicsaxguy commented Jan 16, 2026

Overview

containerization and Kubernetes-ready deployment for JMAP Webmail with:

  • Multi-stage Docker image optimized for Next.js production
  • Environment-driven configuration (no hardcoded values)
  • Structured logging system with text/JSON output
  • Automated CI/CD with GitHub Actions to GHCR
  • Complete Kubernetes deployment manifests with security best practices

Key Changes

Docker & Container Image

  • Dockerfile: Multi-stage build (Node 22 Alpine) with:

    • Non-root user execution (UID/GID 1001)
    • Pinned npm version (11.7.0) for lock file consistency
    • Health checks via /api/health endpoint
    • 512MB Node.js heap allocation for Next.js
    • Container security labels and metadata
  • .dockerignore: Excludes build artifacts, git, node_modules for minimal image size

  • docker-compose.yml: Complete local testing environment with:

    • 95+ lines of inline environment variable documentation
    • Health check configuration
    • Memory allocation and logging setup
    • Non-root user enforcement

Configuration (No Hardcoded Values)

  • .env.example: Comprehensive documentation of all 40+ environment variables:

    • JMAP_SERVER_URL (required)
    • APP_NAME, NODE_ENV, PORT, HOSTNAME
    • TZ/TIMEZONE (replaced hardcoded Europe/Paris)
    • HEALTH_MEMORY_WARNING_THRESHOLD, HEALTH_MEMORY_CRITICAL_THRESHOLD
    • LOG_LEVEL (error/warn/info/debug), LOG_FORMAT (text/json)
    • NODE_OPTIONS for memory control
  • i18n/request.ts: Timezone now uses env vars instead of hardcoded 'Europe/Paris'

Structured Logging

  • lib/logger.ts: New utility providing:

    • Configurable LOG_LEVEL (error/warn/info/debug)
    • LOG_FORMAT support (text for humans, json for log aggregation)
    • Request logging helpers
    • Colored output for development
  • lib/server-init.ts: Startup logging showing:

    • Full configuration on boot
    • Warnings if JMAP_SERVER_URL not set
    • Health check threshold settings
    • Node.js version and environment details
  • api/health/route.ts: Enhanced with:

    • Configurable memory thresholds
    • Structured error/warning logging
    • Detailed memory usage reporting
  • api/config/route.ts: Added logging for debugging

  • app/layout.tsx: Imported server-init for startup logging

Kubernetes Deployment

  • kubernetes-deployment.yaml: Production-ready manifests:
    • Secret for JMAP_SERVER_URL
    • ConfigMap for non-sensitive configuration
    • Deployment with 2 replicas, rolling updates
    • Health probes (liveness/readiness/startup)
    • Resource limits (100m CPU/256Mi memory request, 500m/512Mi limits)
    • Security context (non-root, CAP_DROP=ALL)
    • Service (ClusterIP) and Ingress configuration
    • HorizontalPodAutoscaler (2-10 replicas on CPU/memory)

GitHub Actions CI/CD

  • .github/workflows/docker-build.yml: Automated Docker builds:
    • Triggers: PRs with path filters (only on relevant file changes)
    • Release trigger for version tagging
    • Multi-platform builds (linux/amd64, linux/arm64)
    • SBOM + Provenance attestations for security scanning
    • Smart tagging strategy:
      • PRs: pr-{number}, sha-{commit}
      • Releases: 1.2.3, 1.2, 1, latest, sha-{commit}
    • GitHub Actions cache for faster builds
    • GHCR login with GITHUB_TOKEN (no PAT needed)
    • Conditional push (builds on PR without pushing)

Documentation

  • DOCKER.md: 300+ lines covering:

    • Quick start with docker-compose
    • GitHub Actions CI/CD workflow explanation
    • Release triggering process
    • Kubernetes deployment guide
    • Health check configuration
    • Logging setup and examples
    • Performance tuning
    • Troubleshooting
  • GHCR.md: 250+ lines covering:

    • GitHub Container Registry setup
    • PAT creation and authentication
    • Kubernetes image pull secrets
    • Image reference patterns
    • Complete deployment examples
    • Security considerations

Dependencies Updated

  • Next.js: 16.0.8 → 16.1.2
  • React: 19.2.1 → 19.2.3
  • next-intl: 4.5.8 → 4.7.0
  • Tailwind CSS: 4.1.17 → 4.1.18
  • eslint-config-next: 16.0.8 → 16.1.2

Version Bump

  • package.json: 0.1.0 → 1.0.0 (first stable release)
  • Removed turbopack from build script for production stability

@theepicsaxguy theepicsaxguy force-pushed the feat--Add-Dockerfile-and-update branch 2 times, most recently from 541191e to 581ec72 Compare January 16, 2026 11:43
theepicsaxguy and others added 12 commits January 16, 2026 12:43
- Add detailed request logging including URL, auth type
- Log full error responses from upstream JMAP server
- Include response body in error logs (truncated to 500 chars)
- Add stack traces for caught exceptions
- Improve error messages with status code and status text

This will help debug the 402 Payment Required error from the upstream
JMAP server.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Critical fixes for JMAP session handling:

1. **Fix 307 redirect**: Add redirect: 'follow' to fetch options to properly
   handle Stalwart's 307 redirect from /.well-known/jmap to /jmap/session

2. **Fix mixed content blocking**: Rewrite HTTP URLs to HTTPS in JMAP session
   response to prevent browser mixed content errors
   - Rewrites apiUrl, downloadUrl, uploadUrl, eventSourceUrl
   - Removes :8080 port from URLs (assuming reverse proxy handles this)
   - Logs each rewrite for debugging

This fixes the 402 error which was actually caused by the browser blocking
mixed content HTTP requests from an HTTPS page.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Security improvements for production deployment:

1. **Content Security Policy (CSP)**:
   - Added strict CSP headers to prevent XSS and injection attacks
   - Allows only trusted sources for scripts, styles, images, and connections
   - Blocks inline scripts/styles except where needed for functionality
   - Prevents framing, object embedding, and other attack vectors
   - Enforces HTTPS upgrade for all insecure requests

2. **Secure URL Rewriting**:
   - Enhanced URL rewriting logic with hostname validation
   - Only rewrites URLs from the configured JMAP server domain
   - Prevents URL injection attacks
   - Validates URLs before rewriting to prevent malformed URL attacks
   - Logs refused rewrites for security monitoring

3. **Additional Security Headers** (already present):
   - X-Frame-Options: DENY
   - X-Content-Type-Options: nosniff
   - Referrer-Policy: no-referrer
   - Strict-Transport-Security with preload
   - Permissions-Policy to block unnecessary browser features

These changes make the application suitable for national security level deployments
by implementing defense-in-depth security practices.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Contributor

@ma2t ma2t left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for putting this together — there's a lot of solid work here. The Dockerfile follows good patterns (multi-stage build, non-root user, health checks), the Kubernetes manifests have proper security context (CAP_DROP ALL, non-root), the GitHub Actions workflow is well-structured with path-based triggers and SBOM attestations, and the documentation in DOCKER.md/GHCR.md is thorough. The timezone env var fix and configurable health thresholds are nice improvements too.

That said, I think this PR needs some rework before we can merge it. Two main areas:

Scope & Architecture

The biggest concern is that this goes well beyond containerization. The PR introduces a server-side JMAP session proxy (/api/jmap/session) and an in-memory auth/session system (/api/auth/login) that fundamentally change how the client connects to JMAP servers — from direct client-to-server to client-through-proxy. That's a significant architectural shift that deserves its own discussion and dedicated PR.

It also creates a concrete problem: the in-memory Map<> session store is per-process, but the K8s manifests configure 2+ replicas with HPA scaling to 10. Requests hitting different pods will lose their session. This would need an external session store (Redis, etc.) or sticky sessions to work in production.

Similarly, the dependency bumps (Next.js, React, next-intl, Tailwind), the version bump to 1.0.0, and removing --turbopack from the build script are all changes that affect the whole project and should be reviewed independently.

Key Issues to Fix

A few things that need addressing regardless of how we split this up:

  • Dockerfile: npm install --frozen-lockfile is a Yarn flag — npm needs npm ci for deterministic installs.
  • docker-compose.yml: JMAP_SERVER_URL=https://mailadmin.peekoff.com looks like a real server URL — should be https://mail.example.com. Also has LOG_LEVEL defined twice.
  • CSP: unsafe-eval is applied unconditionally including production. The comment says it's for dev mode, but there's no conditional — this weakens XSS protection in prod.
  • .dockerignore: Excludes .env*.local but not .env itself, so a .env with secrets could end up in the image.
  • next.config.ts rewrites: The /jmap, /download, /upload, /eventsource rewrites don't forward Authorization headers, so authenticated requests through these paths will fail.

Suggested Path Forward

I'd love to get the containerization parts merged — the Docker/K8s infrastructure is genuinely useful. Would you be open to splitting this into smaller PRs? Something like:

  1. Docker + K8s infra — Dockerfile, docker-compose, K8s manifests, .dockerignore, CI/CD workflow, DOCKER.md, GHCR.md (with the fixes above)
  2. Security headers — The next.config.ts headers (with CSP fix)
  3. Logging system — logger.ts, server-init.ts, and the logging additions to existing routes
  4. JMAP proxy + sessions — This needs a design conversation first since it changes the app architecture
  5. Dependency bumps — Separate for clean regression testing
  6. Timezone fix — One-liner, easy merge

This way we can get the straightforward Docker work in quickly and discuss the architectural changes properly. The version bump to 1.0.0 should be left out — we'll handle versioning on our end when we're ready.

Thanks again for the contribution — looking forward to iterating on this!

@ma2t
Copy link
Contributor

ma2t commented Feb 16, 2026

Thanks for the thorough work here — the Kubernetes manifests, CI/CD pipeline, and structured logging approach are well thought out.

We've just shipped native Docker support in the latest release with a similar approach (multi-stage build, standalone Next.js output, docker-compose, health checks, structured logger). Since there's significant overlap now, I'm going to close this PR to avoid conflicts.

A few notes on differences:

  • We kept the Dockerfile minimal (no pinned npm version, no Node heap allocation flags) — happy to revisit if needed
  • Kubernetes and GitHub Actions CI/CD are interesting for the future but out of scope for now
  • We intentionally avoided adding API proxy routes (/api/auth/login, /api/jmap/session) — the client authenticates directly against the JMAP server via Basic Auth

If you'd like to contribute Kubernetes deployment docs or a CI/CD workflow as a separate PR down the line, that would be welcome. Thanks again for the effort.

@ma2t ma2t closed this Feb 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants