In Part 4 you produced static assets. The next step is to serve those files over HTTP so a browser can open them. A common beginner-friendly way to do that is nginx inside a Docker container: nginx handles web requests, and Docker packages the same setup so it runs consistently on any machine.
How this post flows
- Project setup and mental model
- The minimum
nginx.conflines you need first - A basic Dockerfile using
nginx:alpine - A 10-minute terminal lab
- Common pitfalls and extension points
Reading card
- Estimated time: 15 minutes, including the 10-minute lab
- Prereqs: have run
docker buildanddocker runat least once- After reading: you can explain the “static folder → nginx image → running container” pipeline.
Starting here instead of Part 4? That is fine. You only need a static output folder that contains
index.htmland related assets. Some tools call itbuild/, others call itdist/.
Project setup and mental model
Before reading either file, place them next to your built static assets.
my-static-site/
├── build/
│ ├── index.html
│ └── assets/
├── Dockerfile
└── nginx.conf
Now keep this flow in mind:
1. build/ folder: generated HTML/CSS/JS files
2. Docker image: packages nginx plus those files together
3. Docker container: runs nginx so the files can be served
4. Browser request: reaches nginx through the published port
nginx is the web server application. Docker is the packaging tool that bundles nginx together with your static files. The container is the running instance of that package. nginx does not care which framework produced the files. It simply serves whatever lives under /usr/share/nginx/html.
One more concept matters before the commands: port mapping. Inside the container, nginx listens on port 80. On your own machine, you will usually publish that as a different port such as 8080 with -p 8080:80.
- left side of
8080:80: the port on your machine - right side of
8080:80: the port inside the container
Read the minimum nginx.conf first
The Dockerfile needs nginx.conf, so it helps to understand that file first. You do not need a giant config file. This small server block is enough for many static sites:
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
location / { try_files $uri $uri/ /index.html; }
}
listen 80;means nginx accepts HTTP requests on port 80 inside the container.server_name _;means "accept requests no matter which domain name was used." That is convenient for local testing. In production, replace_with your real domain such asexample.com.root /usr/share/nginx/html;points to the folder nginx will serve.try_files $uri $uri/ /index.html;checks three paths in order: an exact file, a directory index, and finallyindex.html.
That last fallback is important for single-page apps such as React or Vue projects. If a browser requests /about, nginx may not find a literal /about file, so it returns index.html and lets the browser-side router take over.
If you are serving a traditional multi-page site, be careful: the SPA fallback turns many missing paths into index.html responses instead of normal 404 responses. In that case, use this version instead:
location / { try_files $uri $uri/ =404; }
Review the core Dockerfile
Most frontend tools create a folder such as build/ or dist/ when you run their build command. In this post we assume your final static files are already in build/, and that folder contains index.html plus assets.
This Dockerfile copies the static files into nginx and publishes the server on port 80 inside the container:
FROM nginx:alpine
RUN rm -rf /usr/share/nginx/html/*
COPY build/ /usr/share/nginx/html/
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
- We use
nginx:alpinebecause it is a small nginx image that is popular for static-site containers. RUN rm -rf ...clears the default welcome page so you start from a clean document root. In newer nginx images this may already be minimal, but clearing it makes the document root explicit for learners.COPY build/ /usr/share/nginx/html/moves your build artifacts into the document root.COPY nginx.conf ...replaces the default server block in/etc/nginx/conf.d/with your settings.EXPOSE 80documents that nginx listens on port 80 inside the container. It does not publish the port by itself; you still needdocker run -p 8080:80when you start the container.
For a first local deployment, stop there. That is the true minimum.
If you want an optional health check later, add a tool such as wget first and then use a command like this:
RUN apk add --no-cache wget
HEALTHCHECK --interval=30s --timeout=3s --retries=3 CMD wget -q --spider http://127.0.0.1/ || exit 1
Here 127.0.0.1 means "inside the container itself." The health check runs from the container's point of view, not from your laptop.
Production note: This example is intentionally minimal for learning. In production you would usually add stricter security settings, review caching headers carefully, and decide whether you want nginx to run as a non-root user.
Mini lab: build an image and inspect the container
- Run your static site build (
npm run buildor similar) and confirm thebuild/folder exists.
ls -la build/index.html
- Place the example
Dockerfileandnginx.confin the same directory as thatbuild/folder. - Run these commands:
docker build -t my-static-site .
docker run -d --name my-static -p 8080:80 my-static-site
The second command maps port 8080 on your machine to port 80 inside the container. That is why you visit localhost:8080 instead of localhost:80.
- Visit
http://localhost:8080in a browser and confirm it renders. - Check the container with these commands:
docker ps
docker logs my-static
curl -I localhost:8080
docker psshows whether the container is still running.docker logs my-staticshows nginx output and startup errors.curl -I localhost:8080sends a simple HTTP request from your machine.
Tip: If the container is not working, remove it with
docker rm -f my-static, tweak the Dockerfile, rebuild, and try again. Quick "change and retry" cycles matter more than pristine first attempts.
Common mistakes and quick fixes
- Copying the wrong build path: Confirm that
build/really exists on your machine and containsindex.html. Some tools usedist/instead. - Port conflicts: Change the host side of
-p 8080:80to any open port, such as 8787 or 3000. - Single-page app fallback used on a multi-page site: If every broken URL keeps loading the home page, switch
try_files $uri $uri/ /index.html;totry_files $uri $uri/ =404;. - Health check failures: Remember that
127.0.0.1in the health check means "inside the container." From your host machine, test withlocalhost:8080.
If the page is blank or missing, run one extra check inside the container:
docker exec my-static ls /usr/share/nginx/html
If you do not see index.html there, nginx has nothing useful to serve yet.
Frequent extension points for static deployments
Once the basic deployment works, you usually add features to solve a specific problem:
- cache headers so browsers can reuse static files instead of downloading them on every visit
- gzip so large text assets such as HTML, CSS, and JS transfer faster
- redirects for paths like
/koand/enwhen one site serves multiple locales - proxy rules so
/apirequests reach a separate backend service docker composefiles when one container grows into a web + API + database setup
Despite those add-ons, the principle stays the same: copy static files into /usr/share/nginx/html and expose port 80. Master that loop and you can containerize any static site with confidence.
Next post preview: bundle environments with Docker Compose
A single image does not describe an entire service. Part 6 introduces Docker Compose so you can define multi-container environments, starting with the classic web + database combo.
💬 댓글
이 글에 대한 의견을 남겨주세요