## Bug Fixes ### Note Card Actions - Fix broken size change functionality (missing state declaration) - Implement React 19 useOptimistic for instant UI feedback - Add startTransition for non-blocking updates - Ensure smooth animations without page refresh - All note actions now work: pin, archive, color, size, checklist ### Markdown LaTeX Rendering - Add remark-math and rehype-katex plugins - Support inline equations with dollar sign syntax - Support block equations with double dollar sign syntax - Import KaTeX CSS for proper styling - Equations now render correctly instead of showing raw LaTeX ## Technical Details - Replace undefined currentNote references with optimistic state - Add optimistic updates before server actions for instant feedback - Use router.refresh() in transitions for smart cache invalidation - Install remark-math, rehype-katex, and katex packages ## Testing - Build passes successfully with no TypeScript errors - Dev server hot-reloads changes correctly
1215 lines
23 KiB
Markdown
1215 lines
23 KiB
Markdown
# Deployment Guide - Memento Project
|
|
|
|
## Overview
|
|
|
|
Complete deployment guide for the Memento note-taking application using Docker and Docker Compose. Covers production deployment, environment configuration, and service orchestration.
|
|
|
|
**Architecture:** Multi-container deployment with keep-notes (Next.js web app) and mcp-server (Express MCP server)
|
|
|
|
---
|
|
|
|
## Prerequisites
|
|
|
|
### Required Software
|
|
|
|
| Tool | Version | Purpose |
|
|
|------|---------|---------|
|
|
| **Docker** | 20.10+ | Container runtime |
|
|
| **Docker Compose** | 2.0+ | Multi-container orchestration |
|
|
| **Git** | Latest | Clone repository |
|
|
|
|
### Verify Installation
|
|
|
|
```bash
|
|
docker --version
|
|
docker compose version
|
|
```
|
|
|
|
---
|
|
|
|
## Quick Start (Docker Compose)
|
|
|
|
### 1. Clone Repository
|
|
|
|
```bash
|
|
git clone <repository-url>
|
|
cd Keep
|
|
```
|
|
|
|
### 2. Configure Environment
|
|
|
|
```bash
|
|
# Copy environment template
|
|
cp .env.example .env
|
|
|
|
# Edit with your configuration
|
|
nano .env
|
|
```
|
|
|
|
### 3. Start Services
|
|
|
|
```bash
|
|
# Build and start all services
|
|
docker compose up -d
|
|
|
|
# View logs
|
|
docker compose logs -f
|
|
|
|
# Check service status
|
|
docker compose ps
|
|
```
|
|
|
|
### 4. Access Application
|
|
|
|
- **Web Application:** http://localhost:3000
|
|
- **Prisma Studio:** http://localhost:5555 (when running)
|
|
|
|
### 5. Stop Services
|
|
|
|
```bash
|
|
# Stop all services
|
|
docker compose down
|
|
|
|
# Stop and remove volumes (⚠️ deletes data)
|
|
docker compose down -v
|
|
```
|
|
|
|
---
|
|
|
|
## Docker Configuration Files
|
|
|
|
### Root Directory Structure
|
|
|
|
```
|
|
Keep/
|
|
├── docker-compose.yml # Main orchestration
|
|
├── .env # Environment variables
|
|
├── keep-notes/
|
|
│ ├── Dockerfile # Web app container
|
|
│ ├── .dockerignore # Build exclusions
|
|
│ └── next.config.ts # Next.js config
|
|
└── mcp-server/
|
|
├── Dockerfile # MCP server container
|
|
└── .dockerignore # Build exclusions
|
|
```
|
|
|
|
---
|
|
|
|
## Docker Compose Configuration
|
|
|
|
### Complete docker-compose.yml
|
|
|
|
```yaml
|
|
version: '3.8'
|
|
|
|
services:
|
|
# ============================================
|
|
# keep-notes - Next.js Web Application
|
|
# ============================================
|
|
keep-notes:
|
|
build:
|
|
context: ./keep-notes
|
|
dockerfile: Dockerfile
|
|
container_name: memento-web
|
|
ports:
|
|
- "3000:3000"
|
|
environment:
|
|
- DATABASE_URL=file:/app/prisma/dev.db
|
|
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
|
|
- NEXTAUTH_URL=${NEXTAUTH_URL:-http://localhost:3000}
|
|
- NODE_ENV=production
|
|
|
|
# Email Configuration (SMTP)
|
|
- SMTP_HOST=${SMTP_HOST}
|
|
- SMTP_PORT=${SMTP_PORT:-587}
|
|
- SMTP_USER=${SMTP_USER}
|
|
- SMTP_PASS=${SMTP_PASS}
|
|
- SMTP_FROM=${SMTP_FROM:-noreply@memento.app}
|
|
|
|
# AI Providers
|
|
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
|
- OLLAMA_API_URL=${OLLAMA_API_URL:-http://ollama:11434}
|
|
volumes:
|
|
- db-data:/app/prisma
|
|
- uploads-data:/app/public/uploads
|
|
depends_on:
|
|
- ollama
|
|
restart: unless-stopped
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 40s
|
|
networks:
|
|
- memento-network
|
|
|
|
# ============================================
|
|
# mcp-server - MCP Protocol Server
|
|
# ============================================
|
|
mcp-server:
|
|
build:
|
|
context: ./mcp-server
|
|
dockerfile: Dockerfile
|
|
container_name: memento-mcp
|
|
volumes:
|
|
- db-data:/app/db
|
|
depends_on:
|
|
- keep-notes
|
|
restart: unless-stopped
|
|
networks:
|
|
- memento-network
|
|
|
|
# ============================================
|
|
# Ollama - Local LLM Provider (Optional)
|
|
# ============================================
|
|
ollama:
|
|
image: ollama/ollama:latest
|
|
container_name: memento-ollama
|
|
ports:
|
|
- "11434:11434"
|
|
volumes:
|
|
- ollama-data:/root/.ollama
|
|
restart: unless-stopped
|
|
networks:
|
|
- memento-network
|
|
|
|
# ============================================
|
|
# Prisma Studio - Database GUI (Optional)
|
|
# ============================================
|
|
prisma-studio:
|
|
build:
|
|
context: ./keep-notes
|
|
dockerfile: Dockerfile
|
|
container_name: memento-studio
|
|
command: npx prisma studio
|
|
ports:
|
|
- "5555:5555"
|
|
environment:
|
|
- DATABASE_URL=file:/app/prisma/dev.db
|
|
volumes:
|
|
- db-data:/app/prisma
|
|
depends_on:
|
|
- keep-notes
|
|
restart: unless-stopped
|
|
networks:
|
|
- memento-network
|
|
profiles:
|
|
- admin # Only start with: docker compose --profile admin up
|
|
|
|
# ============================================
|
|
# Volumes - Data Persistence
|
|
# ============================================
|
|
volumes:
|
|
db-data:
|
|
driver: local
|
|
uploads-data:
|
|
driver: local
|
|
ollama-data:
|
|
driver: local
|
|
|
|
# ============================================
|
|
# Networks - Service Communication
|
|
# ============================================
|
|
networks:
|
|
memento-network:
|
|
driver: bridge
|
|
```
|
|
|
|
---
|
|
|
|
## Dockerfile: keep-notes
|
|
|
|
### Complete Dockerfile
|
|
|
|
```dockerfile
|
|
# ============================================
|
|
# Multi-stage build for Next.js 16
|
|
# ============================================
|
|
|
|
# Stage 1: Dependencies
|
|
FROM node:20-alpine AS deps
|
|
RUN apk add --no-cache libc6-compat
|
|
WORKDIR /app
|
|
|
|
# Install dependencies based on package manager
|
|
COPY package*.json ./
|
|
RUN npm ci
|
|
|
|
# Stage 2: Builder
|
|
FROM node:20-alpine AS builder
|
|
WORKDIR /app
|
|
COPY --from=deps /app/node_modules ./node_modules
|
|
COPY . .
|
|
|
|
# Generate Prisma client
|
|
RUN npx prisma generate
|
|
|
|
# Build Next.js application
|
|
ENV NEXT_TELEMETRY_DISABLED=1
|
|
RUN npm run build
|
|
|
|
# Stage 3: Production Runner
|
|
FROM node:20-alpine AS runner
|
|
WORKDIR /app
|
|
|
|
ENV NODE_ENV=production
|
|
ENV NEXT_TELEMETRY_DISABLED=1
|
|
|
|
# Create non-root user
|
|
RUN addgroup --system --gid 1001 nodejs
|
|
RUN adduser --system --uid 1001 nextjs
|
|
|
|
# Copy necessary files
|
|
COPY --from=builder /app/public ./public
|
|
COPY --from=builder /app/.next/standalone ./
|
|
COPY --from=builder /app/.next/static ./.next/static
|
|
COPY --from=builder /app/prisma ./prisma
|
|
COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
|
|
COPY --from=builder /app/node_modules/@prisma ./node_modules/@prisma
|
|
|
|
# Create directories with proper permissions
|
|
RUN mkdir -p /app/prisma /app/public/uploads/notes
|
|
RUN chown -R nextjs:nodejs /app
|
|
|
|
USER nextjs
|
|
|
|
EXPOSE 3000
|
|
|
|
ENV PORT=3000
|
|
ENV HOSTNAME="0.0.0.0"
|
|
|
|
# Health check
|
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
|
CMD curl -f http://localhost:3000/api/health || exit 1
|
|
|
|
CMD ["node", "server.js"]
|
|
```
|
|
|
|
### Update next.config.ts for Standalone Output
|
|
|
|
```typescript
|
|
// keep-notes/next.config.ts
|
|
import type { NextConfig } from 'next'
|
|
|
|
const nextConfig: NextConfig = {
|
|
// ... existing config
|
|
|
|
// Enable standalone output for Docker
|
|
output: 'standalone',
|
|
|
|
// Optimize for production
|
|
reactStrictMode: true,
|
|
swcMinify: true,
|
|
|
|
// Image optimization
|
|
images: {
|
|
unoptimized: true, // Required for standalone
|
|
},
|
|
}
|
|
|
|
export default nextConfig
|
|
```
|
|
|
|
### .dockerignore for keep-notes
|
|
|
|
```
|
|
node_modules
|
|
.next
|
|
.git
|
|
.gitignore
|
|
*.md
|
|
.env*.local
|
|
.vscode
|
|
idea
|
|
.DS_Store
|
|
*.log
|
|
coverage
|
|
test-results
|
|
playwright-report
|
|
```
|
|
|
|
---
|
|
|
|
## Dockerfile: mcp-server
|
|
|
|
### Complete Dockerfile
|
|
|
|
```dockerfile
|
|
# Base image
|
|
FROM node:20-alpine
|
|
|
|
# Install dependencies
|
|
WORKDIR /app
|
|
|
|
# Copy package files
|
|
COPY package*.json ./
|
|
|
|
# Install dependencies
|
|
RUN npm ci --only=production
|
|
|
|
# Copy application code
|
|
COPY . .
|
|
|
|
# Copy Prisma schema and client
|
|
COPY prisma ./prisma
|
|
|
|
# Create non-root user
|
|
RUN addgroup -g 1001 -S mcp && \
|
|
adduser -u 1001 -S mcp -G mcp
|
|
|
|
# Create database directory
|
|
RUN mkdir -p /app/db && \
|
|
chown -R mcp:mcp /app
|
|
|
|
USER mcp
|
|
|
|
WORKDIR /app
|
|
|
|
# Expose (not needed for stdio, but useful for SSE variant)
|
|
EXPOSE 3000
|
|
|
|
# Health check
|
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
|
|
CMD node -e "console.log('healthy')" || exit 1
|
|
|
|
# Start MCP server
|
|
CMD ["node", "index.js"]
|
|
```
|
|
|
|
### .dockerignore for mcp-server
|
|
|
|
```
|
|
node_modules
|
|
.git
|
|
.gitignore
|
|
*.md
|
|
.env
|
|
.env.*
|
|
*.log
|
|
.DS_Store
|
|
index-sse.js # Only use main index.js
|
|
README-SSE.md
|
|
N8N-CONFIG.md
|
|
```
|
|
|
|
---
|
|
|
|
## Environment Configuration
|
|
|
|
### .env.example (Root Directory)
|
|
|
|
```bash
|
|
# ============================================
|
|
# Database Configuration
|
|
# ============================================
|
|
DATABASE_URL="file:/app/prisma/dev.db"
|
|
|
|
# ============================================
|
|
# NextAuth Configuration
|
|
# ============================================
|
|
# Generate with: openssl rand -base64 32
|
|
NEXTAUTH_SECRET="your-secret-key-here-min-32-chars"
|
|
NEXTAUTH_URL="http://localhost:3000"
|
|
|
|
# ============================================
|
|
# Email Configuration (SMTP)
|
|
# ============================================
|
|
# Required for password reset and reminders
|
|
SMTP_HOST="smtp.gmail.com"
|
|
SMTP_PORT="587"
|
|
SMTP_USER="your-email@gmail.com"
|
|
SMTP_PASS="your-app-password"
|
|
SMTP_FROM="noreply@memento.app"
|
|
|
|
# ============================================
|
|
# AI Provider Configuration
|
|
# ============================================
|
|
|
|
# OpenAI (Optional - for GPT models)
|
|
OPENAI_API_KEY="sk-..."
|
|
|
|
# Ollama (Optional - for local models)
|
|
OLLAMA_API_URL="http://ollama:11434"
|
|
|
|
# ============================================
|
|
# Application Settings
|
|
# ============================================
|
|
NODE_ENV="production"
|
|
PORT="3000"
|
|
|
|
# ============================================
|
|
# Docker-Specific Settings
|
|
# ============================================
|
|
# These are usually set in docker-compose.yml
|
|
# Keep for local development reference
|
|
```
|
|
|
|
### Generate NEXTAUTH_SECRET
|
|
|
|
```bash
|
|
# Linux/Mac
|
|
openssl rand -base64 32
|
|
|
|
# Windows (PowerShell)
|
|
-join ((48..57) + (65..90) + (97..122) | Get-Random -Count 32 | % {[char]$_})
|
|
```
|
|
|
|
---
|
|
|
|
## Production Deployment
|
|
|
|
### Option 1: Docker Compose (Recommended)
|
|
|
|
```bash
|
|
# 1. Clone repository
|
|
git clone <repository-url>
|
|
cd Keep
|
|
|
|
# 2. Configure environment
|
|
cp .env.example .env
|
|
nano .env # Edit configuration
|
|
|
|
# 3. Start services
|
|
docker compose up -d
|
|
|
|
# 4. Initialize database (first time only)
|
|
docker compose exec keep-notes npx prisma migrate deploy
|
|
|
|
# 5. Create admin user (optional)
|
|
docker compose exec keep-notes node scripts/promote-admin.js your-email@example.com
|
|
|
|
# 6. Check status
|
|
docker compose ps
|
|
docker compose logs -f keep-notes
|
|
```
|
|
|
|
### Option 2: Individual Containers
|
|
|
|
#### Build keep-notes Image
|
|
|
|
```bash
|
|
cd keep-notes
|
|
docker build -t memento-web:latest .
|
|
docker run -d \
|
|
--name memento-web \
|
|
-p 3000:3000 \
|
|
-v $(pwd)/prisma:/app/prisma \
|
|
-e DATABASE_URL=file:/app/prisma/dev.db \
|
|
-e NEXTAUTH_SECRET=your-secret \
|
|
-e NEXTAUTH_URL=http://localhost:3000 \
|
|
memento-web:latest
|
|
```
|
|
|
|
#### Build mcp-server Image
|
|
|
|
```bash
|
|
cd mcp-server
|
|
docker build -t memento-mcp:latest .
|
|
docker run -d \
|
|
--name memento-mcp \
|
|
-v /path/to/keep-notes/prisma:/app/db \
|
|
memento-mcp:latest
|
|
```
|
|
|
|
---
|
|
|
|
## Database Management in Docker
|
|
|
|
### Run Migrations
|
|
|
|
```bash
|
|
# Apply pending migrations
|
|
docker compose exec keep-notes npx prisma migrate deploy
|
|
|
|
# Create new migration
|
|
docker compose exec keep-notes npx prisma migrate dev --name my_changes
|
|
|
|
# Reset database (⚠️ deletes all data)
|
|
docker compose exec keep-notes npx prisma migrate reset
|
|
```
|
|
|
|
### Seed Database (Optional)
|
|
|
|
```bash
|
|
# Create seed file
|
|
# keep-notes/prisma/seed.ts
|
|
|
|
# Run seed
|
|
docker compose exec keep-notes npx prisma db seed
|
|
```
|
|
|
|
### Access Database Directly
|
|
|
|
```bash
|
|
# Open SQLite CLI
|
|
docker compose exec keep-notes sqlite3 /app/prisma/dev.db
|
|
|
|
# Query notes
|
|
SELECT * FROM Note LIMIT 10;
|
|
|
|
# Exit
|
|
.quit
|
|
```
|
|
|
|
### Backup Database
|
|
|
|
```bash
|
|
# Backup database
|
|
docker compose exec keep-notes \
|
|
sqlite3 /app/prisma/dev.db ".backup /app/prisma/backup.db"
|
|
|
|
# Copy backup to host
|
|
docker cp memento-web:/app/prisma/backup.db ./backup-$(date +%Y%m%d).db
|
|
```
|
|
|
|
### Restore Database
|
|
|
|
```bash
|
|
# Copy backup to container
|
|
docker cp ./backup.db memento-web:/app/prisma/restore.db
|
|
|
|
# Restore
|
|
docker compose exec keep-notes \
|
|
sqlite3 /app/prisma/dev.db ".restore /app/prisma/restore.db"
|
|
```
|
|
|
|
---
|
|
|
|
## Service Management
|
|
|
|
### View Logs
|
|
|
|
```bash
|
|
# All services
|
|
docker compose logs -f
|
|
|
|
# Specific service
|
|
docker compose logs -f keep-notes
|
|
docker compose logs -f mcp-server
|
|
|
|
# Last 100 lines
|
|
docker compose logs --tail=100 keep-notes
|
|
```
|
|
|
|
### Restart Services
|
|
|
|
```bash
|
|
# Restart all
|
|
docker compose restart
|
|
|
|
# Restart specific service
|
|
docker compose restart keep-notes
|
|
|
|
# Rebuild and restart
|
|
docker compose up -d --build
|
|
```
|
|
|
|
### Update Services
|
|
|
|
```bash
|
|
# Pull latest code
|
|
git pull
|
|
|
|
# Rebuild and restart
|
|
docker compose up -d --build
|
|
|
|
# Or force rebuild without cache
|
|
docker compose build --no-cache
|
|
docker compose up -d
|
|
```
|
|
|
|
### Scale Services
|
|
|
|
```bash
|
|
# Scale keep-notes (requires load balancer)
|
|
docker compose up -d --scale keep-notes=3
|
|
```
|
|
|
|
---
|
|
|
|
## Reverse Proxy Configuration
|
|
|
|
### Nginx Configuration
|
|
|
|
```nginx
|
|
# /etc/nginx/sites-available/memento
|
|
server {
|
|
listen 80;
|
|
server_name your-domain.com;
|
|
|
|
# Redirect to HTTPS
|
|
return 301 https://$server_name$request_uri;
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl http2;
|
|
server_name your-domain.com;
|
|
|
|
# SSL certificates
|
|
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
|
|
|
|
# Next.js application
|
|
location / {
|
|
proxy_pass http://localhost:3000;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection 'upgrade';
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_cache_bypass $http_upgrade;
|
|
}
|
|
|
|
# WebSocket support (if needed)
|
|
location /ws {
|
|
proxy_pass http://localhost:3000;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
}
|
|
}
|
|
```
|
|
|
|
### Traefik Configuration (Alternative)
|
|
|
|
```yaml
|
|
# docker-compose.yml addition
|
|
services:
|
|
traefik:
|
|
image: traefik:v2.10
|
|
command:
|
|
- "--api.insecure=true"
|
|
- "--providers.docker=true"
|
|
- "--entrypoints.web.address=:80"
|
|
- "--entrypoints.websecure.address=:443"
|
|
ports:
|
|
- "80:80"
|
|
- "443:443"
|
|
- "8080:8080" # Dashboard
|
|
volumes:
|
|
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
|
|
|
keep-notes:
|
|
# ... existing config
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.http.routers.keep-notes.rule=Host(`your-domain.com`)"
|
|
- "traefik.http.routers.keep-notes.entrypoints=websecure"
|
|
- "traefik.http.services.keep-notes.loadbalancer.server.port=3000"
|
|
```
|
|
|
|
---
|
|
|
|
## SSL/TLS Setup
|
|
|
|
### Let's Encrypt with Certbot
|
|
|
|
```bash
|
|
# Install Certbot
|
|
sudo apt install certbot python3-certbot-nginx
|
|
|
|
# Get certificate
|
|
sudo certbot --nginx -d your-domain.com
|
|
|
|
# Auto-renewal (configured automatically)
|
|
sudo certbot renew --dry-run
|
|
```
|
|
|
|
### Self-Signed Certificate (Development)
|
|
|
|
```bash
|
|
# Generate self-signed certificate
|
|
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
|
-keyout /etc/ssl/private/memento-selfsigned.key \
|
|
-out /etc/ssl/certs/memento-selfsigned.crt
|
|
```
|
|
|
|
---
|
|
|
|
## Monitoring & Logging
|
|
|
|
### Container Health
|
|
|
|
```bash
|
|
# Check health status
|
|
docker compose ps
|
|
|
|
# Inspect container
|
|
docker inspect memento-web
|
|
|
|
# Resource usage
|
|
docker stats
|
|
```
|
|
|
|
### Log Aggregation
|
|
|
|
```bash
|
|
# View logs from all services
|
|
docker compose logs > logs-$(date +%Y%m%d).txt
|
|
|
|
# Rotate logs
|
|
logrotate configure in /etc/logrotate.d/docker-compose
|
|
```
|
|
|
|
### Monitoring Tools
|
|
|
|
**Prometheus + Grafana** (Optional):
|
|
```yaml
|
|
# docker-compose.yml addition
|
|
services:
|
|
prometheus:
|
|
image: prom/prometheus
|
|
volumes:
|
|
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
|
ports:
|
|
- "9090:9090"
|
|
|
|
grafana:
|
|
image: grafana/grafana
|
|
ports:
|
|
- "3001:3000"
|
|
volumes:
|
|
- grafana-data:/var/lib/grafana
|
|
```
|
|
|
|
---
|
|
|
|
## Backup & Disaster Recovery
|
|
|
|
### Automated Backup Script
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# backup.sh - Daily backup script
|
|
|
|
BACKUP_DIR="/backups/memento"
|
|
DATE=$(date +%Y%m%d_%H%M%S)
|
|
|
|
# Create backup directory
|
|
mkdir -p "$BACKUP_DIR/$DATE"
|
|
|
|
# Backup database
|
|
docker compose exec keep-notes \
|
|
sqlite3 /app/prisma/dev.db ".backup /app/prisma/backup.db"
|
|
|
|
# Copy to host
|
|
docker cp memento-web:/app/prisma/backup.db \
|
|
"$BACKUP_DIR/$DATE/database.db"
|
|
|
|
# Backup uploads
|
|
docker cp memento-web:/app/public/uploads \
|
|
"$BACKUP_DIR/$DATE/"
|
|
|
|
# Compress
|
|
tar -czf "$BACKUP_DIR/memento-$DATE.tar.gz" -C "$BACKUP_DIR" $DATE
|
|
rm -rf "$BACKUP_DIR/$DATE"
|
|
|
|
# Keep last 7 days
|
|
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +7 -delete
|
|
|
|
echo "Backup completed: memento-$DATE.tar.gz"
|
|
```
|
|
|
|
### Schedule with Cron
|
|
|
|
```bash
|
|
# Edit crontab
|
|
crontab -e
|
|
|
|
# Add daily backup at 2 AM
|
|
0 2 * * * /path/to/backup.sh
|
|
```
|
|
|
|
### Restore from Backup
|
|
|
|
```bash
|
|
# Extract backup
|
|
tar -xzf memento-20240109_020000.tar.gz
|
|
|
|
# Restore database
|
|
docker cp memento-20240109_020000/database.db \
|
|
memento-web:/app/prisma/restore.db
|
|
|
|
docker compose exec keep-notes \
|
|
sqlite3 /app/prisma/dev.db ".restore /app/prisma/restore.db"
|
|
|
|
# Restore uploads
|
|
docker cp memento-20240109_020000/uploads/. \
|
|
memento-web:/app/public/uploads/
|
|
|
|
# Restart services
|
|
docker compose restart
|
|
```
|
|
|
|
---
|
|
|
|
## Security Hardening
|
|
|
|
### 1. Docker Security
|
|
|
|
```yaml
|
|
# docker-compose.yml hardening
|
|
services:
|
|
keep-notes:
|
|
# ... existing config
|
|
security_opt:
|
|
- no-new-privileges:true
|
|
read_only: true
|
|
tmpfs:
|
|
- /tmp
|
|
cap_drop:
|
|
- ALL
|
|
cap_add:
|
|
- NET_BIND_SERVICE
|
|
```
|
|
|
|
### 2. Network Security
|
|
|
|
```yaml
|
|
# Isolate services
|
|
networks:
|
|
memento-network:
|
|
driver: bridge
|
|
internal: false # Set to true to block external access
|
|
ipam:
|
|
config:
|
|
- subnet: 172.20.0.0/16
|
|
```
|
|
|
|
### 3. Secrets Management
|
|
|
|
```bash
|
|
# Use Docker Secrets (Swarm mode)
|
|
echo "your-secret" | docker secret create nextauth_secret -
|
|
|
|
# In docker-compose.yml
|
|
services:
|
|
keep-notes:
|
|
secrets:
|
|
- nextauth_secret
|
|
environment:
|
|
NEXTAUTH_SECRET_FILE: /run/secrets/nextauth_secret
|
|
|
|
secrets:
|
|
nextauth_secret:
|
|
external: true
|
|
```
|
|
|
|
### 4. Update Base Images
|
|
|
|
```bash
|
|
# Update to latest Alpine
|
|
docker pull node:20-alpine
|
|
|
|
# Rebuild
|
|
docker compose build --no-cache
|
|
```
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Container Won't Start
|
|
|
|
```bash
|
|
# Check logs
|
|
docker compose logs keep-notes
|
|
|
|
# Inspect container
|
|
docker inspect memento-web
|
|
|
|
# Check resource usage
|
|
docker stats
|
|
|
|
# Restart Docker daemon
|
|
sudo systemctl restart docker
|
|
```
|
|
|
|
### Database Connection Issues
|
|
|
|
```bash
|
|
# Verify database file exists
|
|
docker compose exec keep-notes ls -la /app/prisma/dev.db
|
|
|
|
# Check permissions
|
|
docker compose exec keep-notes ls -la /app/prisma/
|
|
|
|
# Re-run migrations
|
|
docker compose exec keep-notes npx prisma migrate deploy
|
|
```
|
|
|
|
### Port Conflicts
|
|
|
|
```bash
|
|
# Check what's using port 3000
|
|
sudo lsof -i :3000
|
|
|
|
# Change port in docker-compose.yml
|
|
ports:
|
|
- "3001:3000" # Use 3001 instead
|
|
```
|
|
|
|
### Out of Memory
|
|
|
|
```bash
|
|
# Increase Docker memory limit
|
|
# Docker Desktop -> Settings -> Resources -> Memory: 4GB+
|
|
|
|
# Or add memory limit in docker-compose.yml
|
|
services:
|
|
keep-notes:
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
memory: 2G
|
|
```
|
|
|
|
### Permission Issues
|
|
|
|
```bash
|
|
# Fix file permissions
|
|
docker compose exec keep-notes chown -R nextjs:nodejs /app/prisma
|
|
docker compose exec keep-notes chmod 644 /app/prisma/dev.db
|
|
```
|
|
|
|
---
|
|
|
|
## Performance Optimization
|
|
|
|
### 1. Enable BuildKit
|
|
|
|
```bash
|
|
# Set environment variable
|
|
export DOCKER_BUILDKIT=1
|
|
export COMPOSE_DOCKER_CLI_BUILD=1
|
|
```
|
|
|
|
### 2. Multi-Stage Build
|
|
|
|
Already implemented in Dockerfiles to reduce image size.
|
|
|
|
### 3. Layer Caching
|
|
|
|
```dockerfile
|
|
# Cache node_modules
|
|
COPY package*.json ./
|
|
RUN npm ci
|
|
COPY . .
|
|
```
|
|
|
|
### 4. Resource Limits
|
|
|
|
```yaml
|
|
# docker-compose.yml
|
|
services:
|
|
keep-notes:
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: '1'
|
|
memory: 2G
|
|
reservations:
|
|
cpus: '0.5'
|
|
memory: 1G
|
|
```
|
|
|
|
### 5. Database Optimization
|
|
|
|
```bash
|
|
# Run VACUUM to optimize SQLite
|
|
docker compose exec keep-notes sqlite3 /app/prisma/dev.db "VACUUM;"
|
|
|
|
# Analyze query performance
|
|
docker compose exec keep-notes npx prisma studio
|
|
```
|
|
|
|
---
|
|
|
|
## CI/CD Integration
|
|
|
|
### GitHub Actions Example
|
|
|
|
```yaml
|
|
# .github/workflows/deploy.yml
|
|
name: Deploy to Production
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
|
|
jobs:
|
|
deploy:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
|
|
- name: Login to Docker Hub
|
|
uses: docker/login-action@v2
|
|
with:
|
|
username: ${{ secrets.DOCKER_USERNAME }}
|
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
|
|
- name: Build and push
|
|
run: |
|
|
docker build -t yourusername/memento-web:latest ./keep-notes
|
|
docker push yourusername/memento-web:latest
|
|
|
|
- name: Deploy to server
|
|
uses: appleboy/ssh-action@master
|
|
with:
|
|
host: ${{ secrets.HOST }}
|
|
username: ${{ secrets.USERNAME }}
|
|
key: ${{ secrets.SSH_KEY }}
|
|
script: |
|
|
cd /app/memento
|
|
docker compose pull
|
|
docker compose up -d
|
|
```
|
|
|
|
---
|
|
|
|
## Development vs Production
|
|
|
|
### Development (Local)
|
|
|
|
```bash
|
|
# Use local Next.js dev server
|
|
npm run dev
|
|
|
|
# Hot reload enabled
|
|
# Source maps enabled
|
|
# Detailed error messages
|
|
```
|
|
|
|
### Production (Docker)
|
|
|
|
```bash
|
|
# Use optimized production build
|
|
docker compose up -d
|
|
|
|
# Minified code
|
|
# Optimized images
|
|
# Server-side rendering
|
|
# Health checks enabled
|
|
```
|
|
|
|
---
|
|
|
|
## Migration from Local to Docker
|
|
|
|
### 1. Export Existing Data
|
|
|
|
```bash
|
|
# From local development
|
|
sqlite3 keep-notes/prisma/dev.db ".dump" > backup.sql
|
|
|
|
# Or copy database file
|
|
cp keep-notes/prisma/dev.db ./dev-backup.db
|
|
```
|
|
|
|
### 2. Import to Docker
|
|
|
|
```bash
|
|
# Copy to container
|
|
docker cp ./dev-backup.db memento-web:/app/prisma/dev.db
|
|
|
|
# Verify
|
|
docker compose exec keep-notes sqlite3 /app/prisma/dev.db "SELECT COUNT(*) FROM Note;"
|
|
```
|
|
|
|
### 3. Update Environment
|
|
|
|
```bash
|
|
# Copy .env to .env.production
|
|
cp .env .env.production
|
|
|
|
# Update URLs for production
|
|
NEXTAUTH_URL="https://your-domain.com"
|
|
```
|
|
|
|
---
|
|
|
|
## Update Strategy
|
|
|
|
### Rolling Updates
|
|
|
|
```bash
|
|
# Zero-downtime deployment
|
|
docker compose up -d --no-deps --build keep-notes
|
|
|
|
# Or use multiple instances
|
|
docker compose up -d --scale keep-notes=2
|
|
```
|
|
|
|
### Rollback
|
|
|
|
```bash
|
|
# Revert to previous version
|
|
docker compose down
|
|
git checkout <previous-commit>
|
|
docker compose up -d --build
|
|
```
|
|
|
|
---
|
|
|
|
## Cost Estimation (Cloud Deployment)
|
|
|
|
### Self-Hosted Options
|
|
|
|
| Provider | Instance | Monthly Cost | Capacity |
|
|
|----------|----------|--------------|----------|
|
|
| DigitalOcean | Basic Droplet | $6-12 | 1-2 GB RAM, 1 vCPU |
|
|
| Linode | Nanode | $5 | 1 GB RAM, 1 vCPU |
|
|
| AWS EC2 | t3.micro | $8-15 | 1 GB RAM, 2 vCPU |
|
|
| Google Cloud | e2-micro | $6-12 | 1 GB RAM, 1 vCPU |
|
|
| Hetzner | CX11 | €4.20 | 4 GB RAM, 1 vCPU |
|
|
|
|
### Managed Services
|
|
|
|
| Service | Cost | Notes |
|
|
|---------|------|-------|
|
|
| Render | Free+$7/plan | Free tier available |
|
|
| Railway | $5-20/plan | Simple setup |
|
|
| Fly.io | $0-16/plan | Pay per use |
|
|
| Vercel | $0-20/plan | Next.js optimized |
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
This deployment guide provides:
|
|
- ✅ Complete Docker setup for both services
|
|
- ✅ Docker Compose orchestration
|
|
- ✅ Production-ready configuration
|
|
- ✅ Database management in containers
|
|
- ✅ SSL/TLS setup
|
|
- ✅ Backup and restore procedures
|
|
- ✅ Security hardening
|
|
- ✅ Monitoring and troubleshooting
|
|
- ✅ CI/CD integration
|
|
|
|
**Quick Start:**
|
|
```bash
|
|
git clone <repo> && cd Keep
|
|
cp .env.example .env && nano .env
|
|
docker compose up -d
|
|
```
|
|
|
|
**Access:** http://localhost:3000
|
|
|
|
---
|
|
|
|
## Next Steps
|
|
|
|
After deployment:
|
|
1. Create admin user
|
|
2. Configure AI providers (OpenAI or Ollama)
|
|
3. Set up email for password reset
|
|
4. Configure backups
|
|
5. Enable SSL/TLS for production
|
|
6. Set up monitoring
|
|
7. Review security settings
|