feat: configure Docker deployment with Cloudflare Tunnel for Netcup hosting
Migrated from Cloudflare Pages to self-hosted Docker deployment on Netcup RS 8000 server. - Add optimized multi-stage Dockerfile with Next.js standalone output - Add docker-compose.yml for container orchestration (port 3003) - Add Cloudflare Tunnel configuration for mycofi.earth routing - Add .dockerignore for optimized build context - Add comprehensive DEPLOYMENT.md with setup and troubleshooting guide - Update next.config.mjs: change output from 'export' to 'standalone' Architecture: Internet → Cloudflare Tunnel → Netcup RS 8000 → Docker Container 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
7da4937556
commit
ee51ac8baa
|
|
@ -0,0 +1,57 @@
|
||||||
|
# .dockerignore - Exclude unnecessary files from Docker build context
|
||||||
|
|
||||||
|
# Git
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.github
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
node_modules
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Next.js
|
||||||
|
/.next/
|
||||||
|
/out/
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# Environment files
|
||||||
|
.env*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# Vercel
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
# Typescript
|
||||||
|
*.tsbuildinfo
|
||||||
|
next-env.d.ts
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
README.md
|
||||||
|
DEPLOYMENT.md
|
||||||
|
*.md
|
||||||
|
|
||||||
|
# Docker files (not needed in container)
|
||||||
|
Dockerfile*
|
||||||
|
docker-compose*.yml
|
||||||
|
.dockerignore
|
||||||
|
|
||||||
|
# Cloudflare
|
||||||
|
cloudflared-config.yml
|
||||||
|
|
@ -0,0 +1,312 @@
|
||||||
|
# MycoFi Earth Website - Deployment Guide
|
||||||
|
|
||||||
|
This guide covers deploying the MycoFi Earth website to your Netcup RS 8000 server using Docker and Cloudflare Tunnel.
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
```
|
||||||
|
Internet → Cloudflare DNS → Cloudflare Tunnel → Netcup RS 8000 → Docker Container (Next.js)
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Domain**: mycofi.earth, www.mycofi.earth
|
||||||
|
- **Server**: Netcup RS 8000 G12 Pro (159.195.32.209)
|
||||||
|
- **Container Port**: 3003 (internal: 3000)
|
||||||
|
- **Technology**: Next.js 15.5.4 with standalone output
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
### On Your Netcup Server
|
||||||
|
|
||||||
|
1. **Docker & Docker Compose** installed
|
||||||
|
2. **Cloudflared** (Cloudflare Tunnel daemon) installed
|
||||||
|
3. **Cloudflare Tunnel** created and configured for mycofi.earth
|
||||||
|
|
||||||
|
## Initial Setup
|
||||||
|
|
||||||
|
### 1. Clone Repository to Netcup Server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh netcup
|
||||||
|
cd /opt/websites # or your preferred directory
|
||||||
|
git clone https://gitea.jeffemmett.com/jeffemmett/mycofi-earth-website.git
|
||||||
|
cd mycofi-earth-website
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Build and Deploy with Docker Compose
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build the Docker image
|
||||||
|
docker-compose build
|
||||||
|
|
||||||
|
# Start the container
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# Verify it's running
|
||||||
|
docker-compose ps
|
||||||
|
docker-compose logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
The website should now be accessible at `http://localhost:3003` on the Netcup server.
|
||||||
|
|
||||||
|
### 3. Set Up Cloudflare Tunnel
|
||||||
|
|
||||||
|
#### Option A: If Tunnel Already Exists
|
||||||
|
|
||||||
|
If you already have a Cloudflare Tunnel set up, update the configuration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Copy the config to cloudflared directory
|
||||||
|
sudo cp cloudflared-config.yml /etc/cloudflared/config.yml
|
||||||
|
|
||||||
|
# Restart cloudflared service
|
||||||
|
sudo systemctl restart cloudflared
|
||||||
|
sudo systemctl status cloudflared
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Option B: Create New Tunnel
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install cloudflared (if not already installed)
|
||||||
|
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
|
||||||
|
sudo dpkg -i cloudflared-linux-amd64.deb
|
||||||
|
|
||||||
|
# Authenticate with Cloudflare
|
||||||
|
cloudflared tunnel login
|
||||||
|
|
||||||
|
# Create the tunnel
|
||||||
|
cloudflared tunnel create mycofi-earth-tunnel
|
||||||
|
|
||||||
|
# This creates a credentials file at:
|
||||||
|
# ~/.cloudflared/<TUNNEL-ID>.json
|
||||||
|
|
||||||
|
# Copy your credentials file to the expected location
|
||||||
|
sudo mkdir -p /root/.cloudflared
|
||||||
|
sudo cp ~/.cloudflared/<TUNNEL-ID>.json /root/.cloudflared/mycofi-earth-tunnel.json
|
||||||
|
|
||||||
|
# Copy the configuration
|
||||||
|
sudo cp cloudflared-config.yml /etc/cloudflared/config.yml
|
||||||
|
|
||||||
|
# Update the config with your actual tunnel ID
|
||||||
|
sudo nano /etc/cloudflared/config.yml
|
||||||
|
|
||||||
|
# Create DNS records (CNAME)
|
||||||
|
cloudflared tunnel route dns mycofi-earth-tunnel mycofi.earth
|
||||||
|
cloudflared tunnel route dns mycofi-earth-tunnel www.mycofi.earth
|
||||||
|
|
||||||
|
# Run as a service
|
||||||
|
sudo cloudflared service install
|
||||||
|
sudo systemctl start cloudflared
|
||||||
|
sudo systemctl enable cloudflared
|
||||||
|
sudo systemctl status cloudflared
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Configure DNS in Cloudflare Dashboard
|
||||||
|
|
||||||
|
Ensure these DNS records exist in your Cloudflare dashboard:
|
||||||
|
|
||||||
|
| Type | Name | Target | Proxy Status |
|
||||||
|
|-------|------|---------------------------------|--------------|
|
||||||
|
| CNAME | @ | <TUNNEL-ID>.cfargotunnel.com | Proxied |
|
||||||
|
| CNAME | www | <TUNNEL-ID>.cfargotunnel.com | Proxied |
|
||||||
|
|
||||||
|
## Updating the Website
|
||||||
|
|
||||||
|
### Method 1: Manual Update on Server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh netcup
|
||||||
|
cd /opt/websites/mycofi-earth-website
|
||||||
|
|
||||||
|
# Pull latest changes
|
||||||
|
git pull
|
||||||
|
|
||||||
|
# Rebuild and restart
|
||||||
|
docker-compose build
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# Check logs
|
||||||
|
docker-compose logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Method 2: Automated CI/CD (Future)
|
||||||
|
|
||||||
|
Set up a GitHub Action or Gitea Action to automatically deploy on push to main:
|
||||||
|
|
||||||
|
1. Trigger on push to main branch
|
||||||
|
2. SSH into Netcup server
|
||||||
|
3. Pull latest changes
|
||||||
|
4. Rebuild Docker image
|
||||||
|
5. Restart container
|
||||||
|
|
||||||
|
## Monitoring
|
||||||
|
|
||||||
|
### Check Container Status
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# View running containers
|
||||||
|
docker-compose ps
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker-compose logs -f mycofi-earth-website
|
||||||
|
|
||||||
|
# Check resource usage
|
||||||
|
docker stats mycofi-earth-website
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check Cloudflare Tunnel Status
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check service status
|
||||||
|
sudo systemctl status cloudflared
|
||||||
|
|
||||||
|
# View tunnel logs
|
||||||
|
sudo journalctl -u cloudflared -f
|
||||||
|
|
||||||
|
# Test tunnel connectivity
|
||||||
|
cloudflared tunnel info mycofi-earth-tunnel
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check Website Accessibility
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# From Netcup server
|
||||||
|
curl http://localhost:3003
|
||||||
|
|
||||||
|
# From internet
|
||||||
|
curl https://mycofi.earth
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Container Won't Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check Docker logs
|
||||||
|
docker-compose logs mycofi-earth-website
|
||||||
|
|
||||||
|
# Rebuild from scratch
|
||||||
|
docker-compose down
|
||||||
|
docker-compose build --no-cache
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cloudflare Tunnel Issues
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Restart cloudflared
|
||||||
|
sudo systemctl restart cloudflared
|
||||||
|
|
||||||
|
# Check configuration
|
||||||
|
sudo cloudflared tunnel info mycofi-earth-tunnel
|
||||||
|
|
||||||
|
# Verify DNS records
|
||||||
|
nslookup mycofi.earth
|
||||||
|
```
|
||||||
|
|
||||||
|
### Port Conflicts
|
||||||
|
|
||||||
|
If port 3003 is already in use, edit `docker-compose.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
ports:
|
||||||
|
- "3005:3000" # Change to an available port
|
||||||
|
```
|
||||||
|
|
||||||
|
Then update `cloudflared-config.yml` to match:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
service: http://localhost:3005
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Optimization
|
||||||
|
|
||||||
|
### Enable Caching
|
||||||
|
|
||||||
|
Next.js standalone output already includes optimizations. Additional caching can be configured in Cloudflare:
|
||||||
|
|
||||||
|
1. Go to Cloudflare Dashboard → Caching
|
||||||
|
2. Enable caching for static assets
|
||||||
|
3. Set cache TTL appropriately
|
||||||
|
|
||||||
|
### Resource Limits
|
||||||
|
|
||||||
|
To limit container resources, add to `docker-compose.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
mycofi-earth-website:
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: '2.0'
|
||||||
|
memory: 1G
|
||||||
|
reservations:
|
||||||
|
cpus: '0.5'
|
||||||
|
memory: 512M
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
1. **Firewall**: Ensure only necessary ports are open (3003 should not be publicly accessible)
|
||||||
|
2. **HTTPS**: Cloudflare Tunnel handles SSL/TLS automatically
|
||||||
|
3. **Environment Variables**: Store sensitive data in `.env` file (not committed to git)
|
||||||
|
4. **Regular Updates**: Keep Docker images and system packages updated
|
||||||
|
|
||||||
|
## Backup Strategy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backup script (run periodically)
|
||||||
|
#!/bin/bash
|
||||||
|
DATE=$(date +%Y%m%d_%H%M%S)
|
||||||
|
BACKUP_DIR="/opt/backups/mycofi-earth"
|
||||||
|
|
||||||
|
mkdir -p $BACKUP_DIR
|
||||||
|
cd /opt/websites/mycofi-earth-website
|
||||||
|
|
||||||
|
# Backup the repository
|
||||||
|
tar -czf $BACKUP_DIR/mycofi-earth-$DATE.tar.gz .
|
||||||
|
|
||||||
|
# Keep only last 7 backups
|
||||||
|
cd $BACKUP_DIR
|
||||||
|
ls -t | tail -n +8 | xargs rm -f
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rollback Procedure
|
||||||
|
|
||||||
|
If an update causes issues:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Stop current version
|
||||||
|
docker-compose down
|
||||||
|
|
||||||
|
# Checkout previous commit
|
||||||
|
git log --oneline # Find previous commit hash
|
||||||
|
git checkout <previous-commit-hash>
|
||||||
|
|
||||||
|
# Rebuild and restart
|
||||||
|
docker-compose build
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## Support & Resources
|
||||||
|
|
||||||
|
- **Next.js Docker Docs**: https://nextjs.org/docs/deployment#docker-image
|
||||||
|
- **Cloudflare Tunnel Docs**: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/
|
||||||
|
- **Internal Docs**: See CLAUDE.md for infrastructure details
|
||||||
|
|
||||||
|
## Migration from Cloudflare Pages
|
||||||
|
|
||||||
|
This deployment replaces the previous Cloudflare Pages setup. Key changes:
|
||||||
|
|
||||||
|
1. **Static Export → Server-Side**: Changed `output: 'export'` to `output: 'standalone'`
|
||||||
|
2. **Cloudflare Pages → Docker Container**: Self-hosted on Netcup RS 8000
|
||||||
|
3. **Direct Cloudflare → Tunnel**: Traffic now routes through Cloudflare Tunnel
|
||||||
|
4. **Benefits**: More control, lower costs, integration with other services on RS 8000
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. Set up automated backups
|
||||||
|
2. Configure monitoring/alerting (Prometheus + Grafana)
|
||||||
|
3. Implement CI/CD pipeline
|
||||||
|
4. Add health checks to docker-compose.yml
|
||||||
|
5. Configure log rotation for Docker logs
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
# Multi-stage Dockerfile for optimized production Next.js deployment
|
||||||
|
# Based on https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile
|
||||||
|
|
||||||
|
# Stage 1: Dependencies
|
||||||
|
FROM node:20-alpine AS deps
|
||||||
|
RUN apk add --no-cache libc6-compat
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install dependencies based on the preferred package manager
|
||||||
|
COPY package.json pnpm-lock.yaml* ./
|
||||||
|
RUN corepack enable pnpm && pnpm i --frozen-lockfile
|
||||||
|
|
||||||
|
# Stage 2: Builder
|
||||||
|
FROM node:20-alpine AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Disable telemetry during build
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
|
# Build the application
|
||||||
|
RUN corepack enable pnpm && pnpm build
|
||||||
|
|
||||||
|
# Stage 3: Runner (production image)
|
||||||
|
FROM node:20-alpine AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
|
# Create non-root user for security
|
||||||
|
RUN addgroup --system --gid 1001 nodejs
|
||||||
|
RUN adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
|
# Copy public assets and built application
|
||||||
|
COPY --from=builder /app/public ./public
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||||
|
|
||||||
|
USER nextjs
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
ENV PORT=3000
|
||||||
|
ENV HOSTNAME="0.0.0.0"
|
||||||
|
|
||||||
|
# Start the Next.js server
|
||||||
|
CMD ["node", "server.js"]
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Cloudflare Tunnel Configuration for MycoFi Earth Website
|
||||||
|
# This configuration routes traffic from mycofi.earth to the Docker container on Netcup RS 8000
|
||||||
|
|
||||||
|
tunnel: mycofi-earth-tunnel
|
||||||
|
credentials-file: /root/.cloudflared/mycofi-earth-tunnel.json
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
# Route mycofi.earth to the local Docker container
|
||||||
|
- hostname: mycofi.earth
|
||||||
|
service: http://localhost:3003
|
||||||
|
|
||||||
|
# Route www.mycofi.earth to the same container
|
||||||
|
- hostname: www.mycofi.earth
|
||||||
|
service: http://localhost:3003
|
||||||
|
|
||||||
|
# Catch-all rule (required by cloudflared)
|
||||||
|
- service: http_status:404
|
||||||
|
|
||||||
|
# Optional: Enable metrics for monitoring
|
||||||
|
metrics: 0.0.0.0:3004
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
mycofi-earth-website:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
image: mycofi-earth-website:latest
|
||||||
|
container_name: mycofi-earth-website
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "3003:3000" # Expose on port 3003 to avoid conflicts with other services
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- HOSTNAME=0.0.0.0
|
||||||
|
- PORT=3000
|
||||||
|
networks:
|
||||||
|
- mycofi-network
|
||||||
|
labels:
|
||||||
|
- "com.docker.compose.project=mycofi-earth"
|
||||||
|
- "description=MycoFi Earth Website - Next.js Application"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
mycofi-network:
|
||||||
|
driver: bridge
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
output: 'export',
|
// Changed from 'export' to 'standalone' for Docker deployment
|
||||||
|
output: 'standalone',
|
||||||
eslint: {
|
eslint: {
|
||||||
ignoreDuringBuilds: true,
|
ignoreDuringBuilds: true,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue