dockerproxyvitecode-server

Vite App with React Fails to Load via Code-Server Proxy: "Loading failed for module" Errors


I am trying to run a React project using Vite inside a Docker container with Code-Server. The app is proxied through Code-Server at http://localhost:8080/proxy/5173/, but I'm facing issues loading the app in the browser.

Here is my Dockerfile:

FROM node:latest
ENV DEBIAN_FRONTEND=noninteractive
RUN apt update
RUN apt install -y curl
WORKDIR /workspace
RUN curl -fsSL https://code-server.dev/install.sh | sh
COPY ./init.sh .
CMD ["bash", "init.sh"]

The init.sh script initializes the project:

#!/bin/bash

# Check if FRAMEWORK is set
if [ -z "$FRAMEWORK" ]; then
    echo "FRAMEWORK is not set"
    exit 1
fi

# Check if PROJECT is set
if [ -z "$PROJECT" ]; then
    echo "PROJECT is not set"
    exit 1
fi

# Create project based on FRAMEWORK
if [ "$FRAMEWORK" = "react" ]; then
    npm create vite@latest "$PROJECT" -- --template react --yes
elif [ "$FRAMEWORK" = "vue" ]; then
    npm create vite@latest "$PROJECT" -- --template vue --yes
else 
    echo "Unknown framework: $FRAMEWORK"
    exit 1
fi

# Install dependencies
cd "$PROJECT" && npm install

# Start Code-Server
code-server --host 0.0.0.0

I run the container with the following command:

docker run -p 8080:8080 -e FRAMEWORK="react" -e PROJECT="my-app" -e PASSWORD="codepwd" vite-node-portos

Vite Configuration

Initially, I used this vite.config.js:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
})

To address the error connect ECONNREFUSED 0.0.0.0:5172, I modified the config to explicitly bind to 0.0.0.0: error image

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  server: {
    host: '0.0.0.0',
    port: 5173,
  },
})

However, this caused errors in the browser console:

Loading failed for the module with source “http://localhost:8080/proxy/5173/src/main.jsx”.
Loading failed for the module with source “http://localhost:8080/proxy/5173/@react-refresh”.
Loading failed for the module with source “http://localhost:8080/proxy/5173/@vite/client”.

I then updated the vite.config.js to include a base configuration:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  server: {
    host: '0.0.0.0',
    port: 5173,
  },
  base: '/proxy/5173/',
})

However, this caused the same errors in the browser console:

Loading failed for the module with source “http://localhost:8080/proxy/5173/src/main.jsx”.
Loading failed for the module with source “http://localhost:8080/proxy/5173/@react-refresh”.
Loading failed for the module with source “http://localhost:8080/proxy/5173/@vite/client”.

error image

How can I resolve the module loading errors and correctly serve my Vite app through the Code-Server proxy?

In tools like Project IDX or GitHub Codespaces, Vite projects work without requiring edits to the vite.config.js file to handle proxy paths. Is there a similar way to resolve the proxy issue with Code-Server, so I don't need to modify the Vite config directly?


Solution

  • I fixed this issues with Vite when running a React app in a Docker container using Code-Server and resolved them by setting up a reverse proxy inside the container and using npm run dev -- --host 0.0.0.0. Here's how I fixed the problem.


    Dockerfile

    FROM node:18
    
    # Install necessary packages
    RUN apt-get update && apt-get install -y \
        curl \
        git \
        nginx \
        nano \
        supervisor \
        && rm -rf /var/lib/apt/lists/*
    
    # Install openvscode-server
    ENV VSCODE_VERSION=1.84.2
    ENV ARCH=x64
    RUN curl -L "https://github.com/gitpod-io/openvscode-server/releases/download/openvscode-server-v${VSCODE_VERSION}/openvscode-server-v${VSCODE_VERSION}-linux-${ARCH}.tar.gz" | tar -xz -C /opt/
    RUN ln -s /opt/openvscode-server-v${VSCODE_VERSION}-linux-${ARCH} /opt/openvscode-server
    
    # Set working directory
    WORKDIR /workspace
    
    # Copy configuration files
    COPY ./supervisord.conf /etc/supervisor/conf.d/supervisord.conf
    COPY ./init.sh .
    COPY ./generate-nginx-conf.sh /generate-nginx-conf.sh
    RUN chmod +x /generate-nginx-conf.sh
    RUN chmod +x init.sh
    
    # Expose only the main port
    EXPOSE 80
    
    CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
    

    init.sh

    This script initializes the React project and starts the Vite development server.

    #!/bin/bash
    
    # Check if FRAMEWORK is set
    if [ -z "$FRAMEWORK" ]; then
        echo "FRAMEWORK is not set"
        exit 1
    fi
    
    # Check if PROJECT is set
    if [ -z "$PROJECT" ]; then
        echo "PROJECT is not set"
        exit 1
    fi
    
    # Create project based on FRAMEWORK
    if [ "$FRAMEWORK" = "react" ]; then
        npm create vite@latest "$PROJECT" -- --template react --yes
    elif [ "$FRAMEWORK" = "vue" ]; then
        npm create vite@latest "$PROJECT" -- --template vue --yes
    else 
        echo "Unknown framework: $FRAMEWORK"
        exit 1
    fi
    
    cd $PROJECT
    npm install
    npm run dev -- --host 0.0.0.0
    

    generate-nginx-conf.sh

    This script generates the nginx configuration dynamically for both the Vite app and Code-Server.

    #!/bin/bash
    
    # Set default values if not provided
    VSCODE_DOMAIN=${VSCODE_DOMAIN:-"reactapp.localhost"}
    APP_DOMAIN=${APP_DOMAIN:-"reactapp-port-5173.localhost"}
    APP_PROXY_PASS=${APP_PROXY_PASS:-"http://localhost:5173"}
    
    # Generate nginx configuration
    cat > /etc/nginx/nginx.conf << EOL
    user www-data;
    worker_processes auto;
    pid /run/nginx.pid;
    
    events {
        worker_connections 1024;
    }
    
    http {
        include /etc/nginx/mime.types;
        default_type application/octet-stream;
        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;
        
        map \$http_upgrade \$connection_upgrade {
            default upgrade;
            '' close;
        }
        
        server {
            listen 80;
            server_name ${VSCODE_DOMAIN};
            access_log /var/log/nginx/vscode.access.log;
            
            location / {
                proxy_pass http://127.0.0.1:3001;
                proxy_set_header Host \$host;
                proxy_set_header X-Real-IP \$remote_addr;
                proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto \$scheme;
                proxy_set_header Upgrade \$http_upgrade;
                proxy_set_header Connection \$connection_upgrade;
                proxy_buffering off;
                proxy_http_version 1.1;
                proxy_connect_timeout 300s;
                proxy_send_timeout 300s;
                proxy_read_timeout 300s;
            }
        }
        
        server {
            listen 80;
            server_name ${APP_DOMAIN};
            
            location / {
                proxy_pass ${APP_PROXY_PASS};
                proxy_http_version 1.1;
                proxy_set_header Upgrade \$http_upgrade;
                proxy_set_header Connection 'upgrade';
                proxy_set_header Host \$host;
                proxy_cache_bypass \$http_upgrade;
                proxy_set_header X-Real-IP \$remote_addr;
                proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto \$scheme;
            }
        }
    }
    EOL
    
    # Test nginx configuration
    nginx -t
    

    supervisord.conf

    This supervisord configuration ensures both Nginx and Code-Server run inside the container.

    [supervisord]
    nodaemon=true
    
    [program:nginx]
    command=/usr/sbin/nginx -g 'daemon off;'
    autostart=true
    autorestart=true
    
    [program:code-server]
    command=/opt/openvscode-server/bin/openvscode-server --host 0.0.0.0
    autostart=true
    autorestart=true
    

    Running the Container

    To run the container:

    docker build -t react-app-container .
    docker run -p 80:80 \
        -e FRAMEWORK="react" \
        -e PROJECT="my-app" \
        -e VSCODE_DOMAIN="reactapp.localhost" \
        -e APP_DOMAIN="reactapp-port-5173.localhost" \
        react-app-container