DEV Community

Sohana Akbar
Sohana Akbar

Posted on

Debugging a containerized frontend — my actual workflow

Let me paint you a familiar picture.

You’ve containerized your React/Vue/Svelte app. It works perfectly in localhost:3000. You build the Docker image, run it locally, and... white screen. Or worse: a cryptic nginx 404. Or API calls that mysteriously point to localhost inside the container.

The old me would rebuild the image 47 times with different console.log statements.

The current me has a workflow. Let me show you what actually works when debugging a frontend app inside a container.

The golden rule: Don’t rebuild what you can inspect
Your first instinct might be to change a line of code, run docker build -t my-app ., then docker run.... Stop. That’s the slow path.

Instead, think of your running container as a live VM that happens to serve JavaScript. You can poke it, open a shell inside it, even hot-swap files.

Workflow step 1: Override the entrypoint for exploration
When something’s broken at startup (env vars not loaded, wrong Nginx config), I don’t run the normal command. I run a shell:

bash
docker run -it --rm my-app /bin/sh
Now I’m inside the built container, but without the webserver starting. I can:

ls -la /usr/share/nginx/html

cat /etc/nginx/conf.d/default.conf

env | grep VITE_ (check if build-time env vars exist)

This alone solves 80% of “why isn’t my app loading?” mysteries.

Workflow step 2: Map your source code as a volume (yes, even in production-like testing)
For active debugging, I never use the image’s static files. I override them:

bash
docker run -p 8080:80 \
-v $(pwd)/dist:/usr/share/nginx/html \
-v $(pwd)/nginx.conf:/etc/nginx/conf.d/default.conf \
my-app
Now I can rebuild dist on my host (with watch mode) and just refresh the browser. The container sees the new files immediately.

Pro tip: For dev servers (Vite, CRA), bind-mount your source and node_modules:

bash
docker run -p 3000:3000 \
-v $(pwd):/app \
-v /app/node_modules \
my-app-dev
The second volume keeps container-native node_modules from being overwritten by your host’s (possibly different) OS.

Workflow step 3: The nuclear option — docker exec with live edit
Sometimes you need to test a one-line fix without waiting for a rebuild. You can edit files inside a running container:

bash
docker exec -it my-running-container /bin/sh
vi /usr/share/nginx/html/index.html # (vi might not be installed — use sed or echo)

Or:

echo 'console.log("debug")' >> /usr/share/nginx/html/index.html
Is it dirty? Yes. Does it save me 2 minutes of rebuild time? Every single time.

Workflow step 4: Debugging network & API calls
Frontend containers usually die because of API URLs. Inside the container, localhost means the container itself, not your host machine.

Here’s my actual checklist when APIs return 500 or CORS errors:

Exec into the container
docker exec -it my-container /bin/sh

Test the API from inside
wget -O- http://backend-service:8080/api/health
or if wget isn’t there:
cat < /dev/tcp/backend-service/8080

Check the built JS bundle
grep -r "API_BASE_URL" /usr/share/nginx/html/main*.js
→ You’ll immediately see if the wrong URL got baked in at build time.

Inspect runtime env vars
Many frontend frameworks embed env vars at build time, not runtime. That’s a whole separate pain, but I verify with:
docker run --rm my-app cat /usr/share/nginx/html/env-config.js (if you use runtime config injection).

Workflow step 5: The killer trick — hot-patching Nginx config
Nginx misconfigurations are the silent killers of containerized SPAs. You try to refresh a deep route like /dashboard/users and get a 404.

Instead of rebuilding, I edit the config live:

bash
docker exec -it my-container vi /etc/nginx/conf.d/default.conf

Add: try_files $uri $uri/ /index.html;

docker exec my-container nginx -s reload
Refresh. Fixed. Now commit that change to your nginx.conf in the repo.

Real example: fixing a broken production image in 2 minutes
Last week, my docker run -p 80:80 my-app-frontend showed nothing but a blank screen with Failed to load module.

Old workflow: rebuild with more logging (8 minutes).
My workflow:

bash

Step 1: Inspect the container's console

docker logs my-running-container # Saw: "Uncaught SyntaxError: Cannot use import statement outside a module"

Step 2: Exec in and check file types

docker exec -it my-container /bin/sh
file /usr/share/nginx/html/assets/index-*.js

→ ASCII text, not JavaScript → MIME type wrong

Step 3: Check nginx config

cat /etc/nginx/conf.d/default.conf # Missing 'types' block or wrong include

Step 4: Live patch

echo 'include /etc/nginx/mime.types;' >> /etc/nginx/conf.d/default.conf
nginx -s reload
Fixed. Total time: 2 minutes. I committed the fix to my Dockerfile’s nginx.conf and moved on.

The takeaway
Debugging a containerized frontend isn’t about perfect code. It’s about lowering the friction to inspect the running system.

Instead of … Do this …
docker build docker run --rm -it my-app /bin/sh
Rebuilding to test a config docker exec + live edit
Guessing API URLs wget from inside the container
Restarting for every change Bind-mount your dist folder
You don’t need a complex orchestration tool. You need a terminal and these five commands.

Now go break something in a container — on purpose — and debug it my way. You’ll thank yourself at 4 PM on a Friday.

What’s your most painful “white screen of death” story with Docker + frontend? Drop it in the comments — misery loves company. 🐳💔

Top comments (0)