Fullstack Docker Application - Complete Build Guide
This guide walks you through building a fullstack application from scratch with Express backend, React frontend, and Docker containerization - ready for production deployment.
Table of Contents
Backend Setup
Express.js server configuration
Frontend Setup
Vite + React application
CORS & Proxy
Understanding cross-origin requests
Docker Backend
Containerizing Express server
Docker Frontend
Multi-stage build for React
Docker Compose
Orchestrating full stack
Backend Setup (Express.js)
Initialize Node.js Project
Create a new directory for the backend:Install Dependencies
Install Express and CORS:Configure package.json
Updatepackage.json to use ES modules:
Create Express Server
Createindex.js:
Test the Backend
Run the server:Frontend Setup (Vite + React)
Initialize Vite Project
Create a new directory and initialize Vite with React:Create API-Fetching Component
Updatesrc/App.jsx:
Run Development Server
http://localhost:5173 (default Vite port).
Understanding CORS and Vite Proxy
What is CORS?
Cross-Origin Resource Sharing (CORS) is a browser security feature that blocks requests between different origins (different protocol, domain, or port).Option A: Vite Proxy (Development Only)
Option A: Vite Proxy (Development Only)
Vite can proxy API requests during development, making them appear to come from the same origin.Advantages:
- No CORS issues in development
- Simple relative URLs
- Works seamlessly with Vite dev server
- Only works during development (
npm run dev) - Not available in production build
Option B: Backend CORS Configuration (Production)
Option B: Backend CORS Configuration (Production)
Configure the backend to allow requests from specific origins.Advantages:
- Works in production
- Proper security configuration
- Full control over allowed origins
- Production builds
- Direct API calls from browsers
- Different deployment scenarios
Vite Proxy Setup
Updatevite.config.js:
- Requests to
/api/*from the frontend are proxied tohttp://localhost:4000 - Browser sees requests as same-origin (
localhost:5173) - No CORS issues during development
Update Frontend Code (With Proxy)
With proxy enabled, updateApp.jsx:
Fixing CORS on Backend
Why Backend CORS is Necessary
Even with Vite proxy for development, you need backend CORS configuration for:Production builds
Static files served separately
Direct API calls
From browsers without proxy
Deployment scenarios
Different hosting configurations
Configure Specific Origins
For better security, specify allowed origins:Dynamic CORS with Environment Variables
For flexibility across environments:Dockerizing the Backend
Create Backend Dockerfile
Createserver/Dockerfile:
Dockerfile Explanation
Dockerfile Explanation
- FROM node:22-alpine: Lightweight Node.js image
- WORKDIR /app: Set working directory
- Copy
package*.jsonfirst for better Docker layer caching - EXPOSE 4000: Document the port (doesn’t actually publish it)
- CMD: Command to run when container starts
Important: Bind to 0.0.0.0
Updateserver/index.js to bind to all interfaces:
Create .dockerignore
Createserver/.dockerignore:
Build and Test Backend Docker Image
Dockerizing the Frontend
Create Frontend Dockerfile (Multi-stage Build)
Createclient/Dockerfile:
Multi-stage Build Benefits:
- Build stage: Compiles React app with all dev dependencies
- Production stage: Serves static files with minimal dependencies
- Result: Significantly smaller final image size
Create .dockerignore for Frontend
Createclient/.dockerignore:
Build and Test Frontend Docker Image
http://localhost:5174 in browser
Update Frontend for Production
For production, updatesrc/App.jsx to use environment variables:
.env.production:
Docker Compose Setup
Create docker-compose.yml
Createdocker-compose.yml in the project root:
Understanding Docker Compose Configuration
Server Service
Server Service
build: ./server: Build context for server Dockerfilecontainer_name: Custom container nameports: "4000:4000": Map host port 4000 to container port 4000networks: Assign to custom networkrestart: Auto-restart policy
Client Service
Client Service
build: ./client: Build context for client Dockerfileports: "5174:3000": Map host port 5174 to container port 3000depends_on: Wait for server to start firstnetworks: Same network as server for communication
Networks
Networks
fullstack-net: Custom bridge network- Allows containers to communicate by service name
- Provides isolation from other Docker networks
Important Networking Concepts
Container-to-Container
Within Docker network, containers can communicate using service names:
Browser-to-Container
Browsers cannot use Docker service names. They must use:
Run with Docker Compose
Update CORS for Production Deployment
When deploying to a server with a public IP, update backend CORS:Production Considerations
Environment Variables
Create.env for Docker Compose:
docker-compose.yml to use env vars:
Health Checks
Add health checks to ensure services are ready:Complete File Structure
Common Issues and Solutions
Issue 1: CORS Error in Browser
Issue 1: CORS Error in Browser
Problem: Browser blocks API requests from different origin.Solution:
- Configure CORS on backend with correct origins
- Ensure production IP/domain is in allowed origins list
Issue 2: Can't Access Container from Browser
Issue 2: Can't Access Container from Browser
Problem: Container running but can’t access on
localhost.Solutions:- Ensure Express binds to
0.0.0.0(notlocalhost) - Check port mappings in docker-compose.yml
- Verify firewall settings
Issue 3: Container-to-Container Communication Not Working
Issue 3: Container-to-Container Communication Not Working
Problem: Services can’t communicate using service names.Solution:
- Ensure both services are on the same network
- Use service names (not
localhost) within containers - Check network configuration in docker-compose.yml
Issue 4: Frontend Can't Connect to Backend
Issue 4: Frontend Can't Connect to Backend
Problem: Frontend build can’t reach backend API.Solutions:
- Use environment variables for API URL
- Update CORS to include production origins
- Verify backend is accessible from browser (not just containers)
Quick Reference Commands
Summary
This guide covered:Express Backend
Setup with CORS configuration
React Frontend
Vite setup with API integration
CORS & Proxy
Development and production strategies
Docker Backend
Containerization best practices
Docker Frontend
Multi-stage builds
Docker Compose
Full-stack orchestration
Key Takeaways
- CORS is essential for browser-based API calls
- Vite proxy helps in development but production needs backend CORS
- Docker requires binding to
0.0.0.0for external access - Service names work for container-to-container communication
- Browsers need
localhostor public IPs, not Docker service names
Happy Coding! 🚀

