Building Your Portfolio Experience
Loading Tariqul Islam's projects...
Loading Tariqul Islam's projects...
Host your Next.js 15+ App Router project on Hostinger VPS with NGINX, Docker, and HTTPS. Secure and production-ready.
Hostinger VPS (Ubuntu 22.04+)
Domain (e.g. domain-name.com)
GitHub repo with your Next.js project
Docker & Docker Compose installed
ssh root@your-vps-ip
sudo apt update && sudo apt upgrade -y# Docker & Compose
curl -fsSL https://get.docker.com | bash apt install docker-compose-plugin -y
# Git
sudo apt install git -y
# Firewall
ufw allow OpenSSH
ufw allow 80,443/tcp
ufw enableAt your domain provider, configure A/AAAA records:
Type | Name | Value |
|---|---|---|
A | @ | VPS IPv4 |
A | www | VPS IPv4 |
AAAA | @ | VPS IPv6 |
AAAA | www | VPS IPv6 |
Note: IPv6 (AAAA records) is optional — only add them if your VPS provider supports and assigns an IPv6 address.
Traefik will handle SSL (Let’s Encrypt) & routing for all apps.
mkdir -p /srv/apps/traefik/dynamic cd /srv/apps/traefiktraefik.yml
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: proxy
watch: true
certificatesResolvers:
letsencrypt:
acme:
email: tariqul@tariqul.dev
storage: /acme.json
httpChallenge:
entryPoint: web
api:
dashboard: true
log:
level: INFO
accessLog:
format: jsondocker-compose.yml
networks:
proxy:
external: true
web:
external: true
services:
traefik:
image: traefik:v3.3
container_name: traefik
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/traefik.yml:ro
- ./acme.json:/acme.json
networks:
- proxy
- web
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.routers.dashboard.rule=Host(`traefik.naturalsefa.com`)"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.tls=true"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$2y$$05$$mdANwdxZ8xVrhRpHFlTlS.M1uD95F1S5CiX2snEa9YQHIwlJW9vg."
- "traefik.http.middlewares.dashboard-redirect.redirectregex.regex=^/$"
- "traefik.http.middlewares.dashboard-redirect.redirectregex.replacement=/dashboard/"
- "traefik.http.middlewares.dashboard-redirect.redirectregex.permanent=true"
- "traefik.http.routers.dashboard.middlewares=dashboard-redirect,dashboard-auth"Initialize:
touch acme.json && chmod 600 acme.json docker network create proxy docker compose up -dmkdir -p ~/srv/apps && cd ~/srv/apps git clone git@github.com:yourusername/your-next-app.git cd your-next-appIf private repo:
ssh-keygen -t ed25519 -C "[email protected]" cat ~/.ssh/id_ed25519.pubAdd this key to GitHub > Settings > Deploy Keys.
Set environment variables:
nano .envDockerfile
# ---- Build stage ---- FROM node:20-alpine AS builder WORKDIR /app # build-time flag (default 1 to skip DB calls) ARG SKIP_DB=1 ENV SKIP_DB=$SKIP_DB ENV NEXT_TELEMETRY_DISABLED=1 COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # ---- Production runtime ---- FROM node:20-alpine WORKDIR /app ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 ENV PORT=3000 ENV HOSTNAME=0.0.0.0 COPY --from=builder /app/public ./public COPY --from=builder /app/.next ./.next COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/package*.json ./ EXPOSE 3000 CMD ["npm", "start"]next.config.ts:
import type { NextConfig } from "next"; const nextConfig: NextConfig = { output: "standalone", }; export default nextConfig;.dockerignore:
node_modules .next .git Dockerfile docker-compose.yml README.mddocker-compose.yml
networks: proxy: external: true appnet: external: true services: next-app: build: context: . dockerfile: ./Dockerfile container_name: next-app env_file: - ./.env environment: - NODE_ENV=production - PORT=3000 - NEXT_TELEMETRY_DISABLED=1 expose: - "3000" healthcheck: test: ["CMD-SHELL", "wget -q --spider http://localhost:3000/ || exit 1"] interval: 30s timeout: 5s retries: 5 start_period: 20s restart: unless-stopped networks: - proxy - appnet labels: - "traefik.enable=true" - "traefik.docker.network=proxy" # Apex domain - "traefik.http.routers.naturalsefa.rule=Host(`naturalsefaa.com`)" - "traefik.http.routers.naturalsefa.entrypoints=websecure" - "traefik.http.routers.naturalsefa.tls.certresolver=letsencrypt" - "traefik.http.services.naturalsefa.loadbalancer.server.port=3000" # www → apex redirect (HTTPS) - "traefik.http.routers.naturalsefa-www.rule=Host(`www.naturalsefaa.com`)" - "traefik.http.routers.naturalsefa-www.entrypoints=websecure" - "traefik.http.routers.naturalsefa-www.tls.certresolver=letsencrypt" - "traefik.http.routers.naturalsefa-www.middlewares=redirect-to-apex@docker" - "traefik.http.middlewares.redirect-to-apex.redirectregex.regex=^https?://www\\.naturalsefaa\\.com/(.*)" - "traefik.http.middlewares.redirect-to-apex.redirectregex.replacement=https://naturalsefaa.com/$$1" - "traefik.http.middlewares.redirect-to-apex.redirectregex.permanent=true"cd /srv/apps/portfolio-next docker compose up -d --buildNow your site is live at https://example.com 🎉
cd /srv/apps/portfolio-next git pull origin main docker compose build docker compose up -dDockerized Next.js app → portable, isolated, reproducible
Traefik reverse proxy → auto-HTTPS, multiple apps/domains handled
Clean deploy/update flow → just git pull && docker compose up -d --build