chore: add Docker deployment behind Caddy
Build the static Next.js export in a reproducible container and expose it through the existing Caddy network and upstream service name.
This commit is contained in:
15
.dockerignore
Normal file
15
.dockerignore
Normal file
@@ -0,0 +1,15 @@
|
||||
.git
|
||||
.gitignore
|
||||
.next
|
||||
out
|
||||
node_modules
|
||||
|
||||
.env
|
||||
.env.*
|
||||
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
Dockerfile*
|
||||
compose*.yaml
|
||||
compose*.yml
|
||||
23
DEPLOYMENT.md
Normal file
23
DEPLOYMENT.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Deployment
|
||||
|
||||
This project builds the Next.js static export in a Node container and serves the
|
||||
generated files from Nginx on port 80. The container joins the existing
|
||||
`caddy_proxy` Docker network under the `static-site` service name expected by
|
||||
Caddy.
|
||||
|
||||
## Deploy or update
|
||||
|
||||
From the project directory on the server, run:
|
||||
|
||||
```sh
|
||||
docker compose up --build -d
|
||||
```
|
||||
|
||||
The external `caddy_proxy` network must already exist and Caddy must be attached
|
||||
to it. No host port is published because Caddy is the public entry point and
|
||||
terminates TLS for `amiirkhl.ir` and `www.amiirkhl.ir`.
|
||||
|
||||
If the previous `static-site` container was created from a different Compose
|
||||
project directory, remove that old stack once before the first deployment to
|
||||
avoid a Docker container-name conflict. Updates from this project then use only
|
||||
the command above.
|
||||
21
Dockerfile
Normal file
21
Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
FROM node:22-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci
|
||||
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx:alpine AS runtime
|
||||
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --from=builder /app/out/ /usr/share/nginx/html/
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD wget --no-verbose --tries=1 --spider http://127.0.0.1/ || exit 1
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
15
compose.yaml
Normal file
15
compose.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
services:
|
||||
static-site:
|
||||
image: amiirkhl-portfolio:latest
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: static-site
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- caddy_proxy
|
||||
|
||||
networks:
|
||||
caddy_proxy:
|
||||
external: true
|
||||
name: caddy_proxy
|
||||
41
nginx.conf
Normal file
41
nginx.conf
Normal file
@@ -0,0 +1,41 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
charset utf-8;
|
||||
server_tokens off;
|
||||
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_types
|
||||
application/javascript
|
||||
application/json
|
||||
application/xml
|
||||
image/svg+xml
|
||||
text/css
|
||||
text/plain;
|
||||
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
|
||||
location ^~ /_next/static/ {
|
||||
try_files $uri =404;
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri.html $uri/ =404;
|
||||
}
|
||||
|
||||
error_page 404 /404.html;
|
||||
|
||||
location = /404.html {
|
||||
internal;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user