# Dockerfile - Peikarband Landing Application # Optimized multi-stage build using base image # Build arguments ARG BASE_IMAGE=hub.peikarband.ir/peikarband/landing:base ARG VERSION=latest ARG BUILD_DATE # ============================================ # Stage 1: Builder (using base image) # ============================================ FROM ${BASE_IMAGE} AS builder LABEL stage=builder LABEL maintainer="Peikarband DevOps " WORKDIR /build # Base image already has: # - Python 3.11 # - Node.js 20 # - bun # - gcc, g++, make, curl, ca-certificates # Verify tools are available RUN echo "=== Build Environment ===" && \ python --version && \ node --version && \ npm --version && \ bun --version && \ echo "========================" # ============================================ # Python Dependencies # ============================================ # Copy Python requirements first (for layer caching) COPY peikarband/requirements.txt . # Install Python dependencies RUN --mount=type=cache,target=/root/.cache/pip \ pip install --no-cache-dir --upgrade pip && \ pip install --no-cache-dir -r requirements.txt # ============================================ # Frontend Build (Reflex) # ============================================ # Copy source code to /build/peikarband/ to preserve package structure # This ensures peikarband.peikarband module can be found by Reflex COPY peikarband/ /build/peikarband/ # Set PYTHONPATH to include /build (for peikarband package) and /build/peikarband (for src imports) # This allows both peikarband.peikarband and src.* imports to work ENV PYTHONPATH=/build:/build/peikarband # Verify that peikarband.landing can be imported before running reflex # This helps catch import errors early RUN cd /build && \ python3 -c "from peikarband.landing import app; print('✅ peikarband.landing.app imported successfully')" && \ echo "Import test passed" # Initialize Reflex and build frontend from peikarband directory # Reflex needs to run from the directory containing rxconfig.py RUN cd /build/peikarband && \ reflex init --loglevel debug || true && \ reflex export --frontend-only --no-zip --loglevel debug || echo "Export completed with warnings" # Install npm dependencies if .web directory exists # Note: reflex export already builds the frontend, we just need to install deps RUN if [ -d "/build/peikarband/.web" ] && [ -f "/build/peikarband/.web/package.json" ]; then \ echo "Found .web directory with package.json, installing dependencies..." && \ cd /build/peikarband/.web && \ # Remove any existing .npmrc that might override registry rm -f .npmrc && \ # Set npm registry to official registry npm config set registry https://registry.npmjs.org/ && \ npm config set fetch-retry-mintimeout 20000 && \ npm config set fetch-retry-maxtimeout 120000 && \ npm config set fetch-retries 5 && \ npm config set fetch-timeout 300000 && \ # Verify registry is set correctly echo "Using npm registry: $(npm config get registry)" && \ # Install dependencies (reflex export already built the frontend) if [ -f "package-lock.json" ]; then \ npm ci --prefer-offline --no-audit --loglevel verbose || \ (echo "npm ci failed, retrying with npm install..." && npm install --prefer-offline --no-audit --loglevel verbose); \ else \ echo "package-lock.json not found, using npm install..." && \ npm install --prefer-offline --no-audit --loglevel verbose; \ fi && \ echo "Dependencies installed successfully"; \ else \ echo "Warning: .web directory or package.json not found, skipping npm install"; \ fi # ============================================ # Stage 2: Runtime (using base image for Node.js) # ============================================ FROM ${BASE_IMAGE} AS runtime # Re-declare build arguments for this stage ARG BASE_IMAGE=hub.peikarband.ir/peikarband/landing:base ARG VERSION=latest ARG BUILD_DATE ARG GIT_COMMIT ARG GIT_BRANCH ARG BUILD_NUMBER LABEL org.opencontainers.image.title="Peikarband Landing" LABEL org.opencontainers.image.description="Peikarband hosting platform landing page" LABEL org.opencontainers.image.vendor="Peikarband" LABEL org.opencontainers.image.version="${VERSION}" LABEL org.opencontainers.image.created="${BUILD_DATE}" # Create non-root user RUN groupadd -r peikarband && \ useradd -r -g peikarband -u 1000 -m -s /bin/bash peikarband WORKDIR /app # Note: We keep WORKDIR=/app (not /app/peikarband) to avoid Python importing # /app/peikarband/landing/ as the peikarband package incorrectly # The entrypoint script will cd to /app/peikarband before running reflex # Base image already has everything we need: # - Python 3.11 # - Node.js 20 # - curl, ca-certificates # - tini (for proper init) # No additional packages needed! # Copy Python dependencies from builder COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages COPY --from=builder /usr/local/bin /usr/local/bin # Copy application code to /app/peikarband/ to create peikarband.landing structure # With app_name="landing", Reflex expects to find peikarband.landing module COPY --from=builder --chown=peikarband:peikarband /build/peikarband /app/peikarband # Copy entrypoint script COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh RUN chmod +x /usr/local/bin/entrypoint.sh # Create necessary directories RUN mkdir -p /app/data /app/logs /app/uploaded_files && \ chown -R peikarband:peikarband /app # Set proper permissions RUN chmod -R 755 /app && \ chmod -R 777 /app/data /app/logs /app/uploaded_files # Environment variables # PYTHONPATH includes both /app and /app/peikarband # - /app: allows importing peikarband from /app/peikarband/ # - /app/peikarband: allows importing src from /app/peikarband/src/ # This makes both peikarband.landing and src.* imports work correctly # REFLEX_DIR points to the directory containing rxconfig.py ENV PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ PYTHONPATH=/app:/app/peikarband \ PATH="/app/.venv/bin:$PATH" \ REFLEX_DIR=/app/peikarband \ NODE_ENV=production # Health check HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ CMD curl -f http://localhost:${PORT:-3000}/_health || exit 1 # Switch to non-root user USER peikarband # Expose port EXPOSE 3000 8000 # Use tini as init system ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/entrypoint.sh"] # Start application # entrypoint.sh will cd to /app/peikarband and run reflex # PYTHONPATH=/app:/app/peikarband allows Python to find both peikarband and peikarband.peikarband CMD ["run", "--env", "prod", "--loglevel", "info", "--frontend-port", "3000", "--backend-port", "8000"] # ============================================ # Build Information # ============================================ # ARG declarations are already done above in runtime stage LABEL git.commit="${GIT_COMMIT}" LABEL git.branch="${GIT_BRANCH}" LABEL build.number="${BUILD_NUMBER}" LABEL build.date="${BUILD_DATE}" # ============================================ # Multi-Architecture Support # ============================================ # This Dockerfile supports: # - linux/amd64 # - linux/arm64 (with appropriate base image) # # Build with: # docker buildx build --platform linux/amd64,linux/arm64 .