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} */
|
||||
const nextConfig = {
|
||||
output: 'export',
|
||||
// Changed from 'export' to 'standalone' for Docker deployment
|
||||
output: 'standalone',
|
||||
eslint: {
|
||||
ignoreDuringBuilds: true,
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue