Best Ways to Host Your React-Based Frontend (Vite, Next.js, Remix) and Backend from One Container

Smiling person in layered hair w/eyelashes,gesturing

Zoia Baletska

18 September 2025

a4dxkv.webp

Modern applications often combine a React-based frontend (built with Vite, Next.js, Remix, etc.) and a backend API (Node.js, Python, Java, etc.). In production, the cleanest architecture is usually to deploy frontend and backend as separate services.

But sometimes you want the simplicity of hosting them together in one container — especially for:

  • Small-to-medium apps

  • Internal tools or MVPs

  • Cost-sensitive projects

  • Hosting providers with single-container limitations

In this article, we’ll explore the best ways to host React-based frontends with a backend in a single Docker container, with examples and pros/cons for each method.

Approach 1: Serve Frontend as Static Files via Backend

For Vite or Remix (static export) projects, the frontend can be built into static files and served directly by your backend.

Example: Vite + Express Backend

Dockerfile:

1# Stage 1: Build React app with Vite
2FROM node:18 AS frontend
3WORKDIR /app/frontend
4COPY frontend/ .
5RUN npm install && npm run build
6     
7# Stage 2: Backend
8FROM node:18
9WORKDIR /app
10COPY backend/ .
11RUN npm install
12     
13# Copy built frontend into backend public folder
14COPY --from=frontend /app/frontend/dist ./public
15CMD ["node", "server.js"]
16

Express server (server.js):

1import express from "express";
2const app = express();
3     
4app.use(express.static("public")); // Serve React build
5      
6app.get("/api/hello", (req, res) => {
7  res.json({ message: "Hello from backend!" });
8});
9     
10app.listen(3000, () => console.log("App running on port 3000"));

✅ Pros

  • Very simple (single process).

  • Works well with Vite/CRA/Remix static builds.

  • Easy CI/CD integration.

❌ Cons

  • Backend takes responsibility for serving static assets → extra load.

  • Not ideal for frameworks needing SSR (Next.js/Remix server mode).

Approach 2: Use Nginx for Frontend + Backend Reverse Proxy

For Next.js (SSR) or Remix (server mode), the backend often needs to run as a Node.js server. A common pattern is to:

  • Use Nginx to serve frontend static files (or reverse proxy SSR routes).

  • Proxy /api requests to the backend.

Dockerfile:

1# Stage 1: Build Next.js app
2FROM node:18 AS frontend
3WORKDIR /app
4COPY frontend/ .
5RUN npm install && npm run build
6      
7# Stage 2: Backend
8FROM node:18 AS backend
9WORKDIR /app/backend
10COPY backend/ .
11RUN npm install
12      
13# Stage 3: Nginx + Backend
14FROM nginx:alpine
15COPY --from=frontend /app/.next /usr/share/nginx/html
16COPY nginx.conf /etc/nginx/conf.d/default.conf
17COPY --from=backend /app/backend /app/backend
18       
19WORKDIR /app/backend
20CMD ["sh", "-c", "node server.js & nginx -g 'daemon off;'"]

nginx.conf:

1server {
2  listen 80;
3      
4  location / {
5    root /usr/share/nginx/html;
6    index index.html;
7  }
8       
9  location /api/ {
10    proxy_pass http://localhost:5000;
11  }
12

✅ Pros

  • Efficient static asset serving with Nginx.

  • Clear separation of frontend vs. backend routes.

  • Great for Next.js or Remix SSR setups.

❌ Cons

  • Two processes in one container (need Supervisor or &).

  • More complex Dockerfile + nginx.conf setup.

Approach 3: Use Next.js or Remix as Both Frontend + Backend

Frameworks like Next.js and Remix already support API routes. This means you can combine frontend and backend logic in one app, then containerise it.

Dockerfile (Next.js fullstack app):

1FROM node:18
2WORKDIR /app
3COPY . .
4RUN npm install && npm run build
5       
6EXPOSE 3000
7CMD ["npm", "start"]

This approach eliminates the need for two separate servers.

✅ Pros

  • One framework handles both frontend and backend.

  • Simplest to containerise.

  • Great for fullstack apps with modest backend needs.

❌ Cons

  • Limited flexibility (harder to migrate backend later).

  • Ties backend tightly to Next.js/Remix.

  • Not suitable for heavy backend workloads.

Approach 4: Multi-Service Setup with Docker Compose

Even if you want “one deployable unit,” it’s often cleaner to use Docker Compose with two services (frontend + backend).

docker-compose.yml:

1version: "3"
2services:
3  frontend:
4    build: ./frontend
5    ports:
6      - "3000:3000"
7  backend:
8    build: ./backend
9    ports:
10      - "5000:5000"
11

✅ Pros

  • Best of both worlds: separation + easy deployment.

  • Backend and frontend can scale independently.

  • Works well in local development and production.

❌ Cons

  • Not technically “one container.”

  • Some hosting providers don’t allow multi-container unless using Kubernetes/Docker Swarm.

Conclusion

There’s no single “best” way — it depends on your stack and goals:

  • Vite/Remix static → Serve via backend (simple).

  • Next.js/Remix SSR → Use Nginx reverse proxy inside the container.

  • Small fullstack apps → Use Next.js/Remix API routes (all-in-one).

  • Production-ready → Use Docker Compose for separation.

👉 For quick prototypes or internal tools: go with Approach 1 or 3.
👉 For scalable production: Approach 2 or 4 is safer.

background

Optimize with ZEN's Expertise