# 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.peikarband can be imported before running reflex # This helps catch import errors early # RUN cd /build && \ # python3 -c "from peikarband.peikarband import app; print('✅ peikarband.peikarband.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 "Frontend export completed" && \ if [ -d .web/node_modules/.bin ]; then \ find .web/node_modules/.bin -type f -exec chmod +x {} \; && \ find .web/node_modules/.bin -type l | while read symlink; do \ target=$(readlink -f "$symlink" 2>/dev/null || true); \ if [ -n "$target" ] && [ -f "$target" ]; then \ chmod +x "$target" 2>/dev/null || true; \ fi; \ chmod +x "$symlink" 2>/dev/null || true; \ done && \ echo "✅ Set executable permissions for all .bin files (files and symlinks) and their targets"; \ fi # Note: reflex export already builds and installs everything needed # No additional npm install is required RUN echo "Frontend built by reflex export" # ============================================ # 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}" # Running as root for now to avoid permission issues # TODO: Switch back to non-root user after permission issues are resolved # 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/peikarband/ 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.peikarband structure # With app_name="peikarband", Reflex expects to find peikarband.peikarband module # Running as root, so no need for chown COPY --from=builder /build/peikarband /app/peikarband # CRITICAL: Remove __init__.py from /app if it exists # Reflex will crash if there's __init__.py in the app root directory RUN if [ -f /app/__init__.py ]; then \ echo "⚠️ WARNING: Removing __init__.py from /app (causes Reflex crash)"; \ rm -f /app/__init__.py; \ fi && \ echo "✅ Verified: No __init__.py in /app root" # Copy entrypoint script COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh RUN chmod +x /usr/local/bin/entrypoint.sh && chmod +x /app/peikarband/.web/app/routes.js # Create necessary directories RUN mkdir -p /app/data /app/logs /app/uploaded_files # Set proper permissions for application files # Explicitly set executable permissions for node_modules/.bin files (both files and symlinks) # Also fix permissions for symlink targets RUN if [ -d /app/peikarband/.web/node_modules/.bin ]; then \ find /app/peikarband/.web/node_modules/.bin -type f -exec chmod +x {} \; && \ find /app/peikarband/.web/node_modules/.bin -type l | while read symlink; do \ target=$(readlink -f "$symlink" 2>/dev/null || true); \ if [ -n "$target" ] && [ -f "$target" ]; then \ chmod +x "$target" 2>/dev/null || true; \ fi; \ chmod +x "$symlink" 2>/dev/null || true; \ done && \ ls -la /app/peikarband/.web/node_modules/.bin/ | head -20 && \ echo "✅ Verified executable permissions for .bin files and symlink targets"; \ fi && \ 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.peikarband 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 # Diagnostic information RUN echo "=== Diagnostic Info ===" && \ if [ -f /app/peikarband/.web/node_modules/.bin/react-router ]; then \ ls -la /app/peikarband/.web/node_modules/.bin/react-router && \ file /app/peikarband/.web/node_modules/.bin/react-router || true; \ fi && \ if [ -f /app/peikarband/.web/node_modules/@react-router/dev/bin.js ]; then \ head -5 /app/peikarband/.web/node_modules/@react-router/dev/bin.js || true; \ fi && \ echo "=======================" # Health check (using backend health endpoint on port 8000) HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ CMD curl -f http://localhost:8000/ping || exit 1 # Running as root for now to avoid permission issues # 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 .