Docker configuration, build file for app

This commit is contained in:
Hendrik Belitz
2025-06-15 15:26:34 +02:00
parent 912966aa15
commit 7e319860b6
9 changed files with 625 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
# Development Environment Configuration
# Copy this file to .env and fill in your values
# Database Configuration
POSTGRES_DB=appointment_booking
POSTGRES_USER=postgres
POSTGRES_PASSWORD=your_secure_password_here
POSTGRES_PORT=5432
# Application Configuration
NODE_ENV=development
APP_PORT=5173
+18
View File
@@ -0,0 +1,18 @@
node_modules/
.env
.env.local
.env.production
build/
dist/
.svelte-kit/
# Production secrets - NEVER commit these
secrets/*.txt
!secrets/.gitkeep
# Development
.DS_Store
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
+31
View File
@@ -0,0 +1,31 @@
# Production Caddyfile for appointment booking application
# Replace your-domain.com with your actual domain
your-domain.com {
reverse_proxy app:3000
# Security headers
header {
-Server
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
X-XSS-Protection "1; mode=block"
Referrer-Policy "strict-origin-when-cross-origin"
Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'"
}
encode gzip
log {
output file /data/access.log {
roll_size 100mb
roll_keep 10
}
format json
}
tls {
protocols tls1.2 tls1.3
}
}
+47
View File
@@ -0,0 +1,47 @@
# Production Dockerfile for SvelteKit application
# Security: rootless container, minimal base image, non-privileged user
# Build stage
FROM node:24-alpine AS builder
WORKDIR /app
# Install dependencies
COPY package*.json ./
RUN npm ci --only=production --ignore-scripts && \
npm cache clean --force
# Copy source and build
COPY . .
RUN npm run build
# Production stage
FROM node:24-alpine AS production
# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S sveltekit -u 1001 -G nodejs
WORKDIR /app
# Copy built application and dependencies
COPY --from=builder --chown=sveltekit:nodejs /app/build ./build
COPY --from=builder --chown=sveltekit:nodejs /app/package*.json ./
COPY --from=builder --chown=sveltekit:nodejs /app/node_modules ./node_modules
# Create logs directory
RUN mkdir -p /app/logs && \
chown -R sveltekit:nodejs /app
# Drop privileges
USER sveltekit
# Expose port
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
# Start application
CMD ["node", "build/index.js"]
+36
View File
@@ -0,0 +1,36 @@
services:
postgres:
image: postgres:16-alpine
container_name: appointment-booking-postgres-dev
restart: unless-stopped
user: postgres
environment:
POSTGRES_DB: ${POSTGRES_DB:-appointment_booking}
POSTGRES_USER: ${POSTGRES_USER:-postgres}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
ports:
- "${POSTGRES_PORT:-5432}:5432"
volumes:
- postgres_data_dev:/var/lib/postgresql/data
- ./init-db:/docker-entrypoint-initdb.d:ro
networks:
- appointment-booking-dev
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-appointment_booking}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
security_opt:
- no-new-privileges:true
tmpfs:
- /tmp
- /var/run/postgresql
volumes:
postgres_data_dev:
driver: local
networks:
appointment-booking-dev:
driver: bridge
+120
View File
@@ -0,0 +1,120 @@
services:
postgres:
image: postgres:16-alpine
container_name: appointment-booking-postgres
restart: unless-stopped
user: postgres
environment:
POSTGRES_DB_FILE: /run/secrets/postgres_db
POSTGRES_USER_FILE: /run/secrets/postgres_user
POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password
POSTGRES_INITDB_ARGS: "--auth-host=scram-sha-256 --auth-local=scram-sha-256"
secrets:
- postgres_db
- postgres_user
- postgres_password
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init-db:/docker-entrypoint-initdb.d:ro
networks:
- appointment-booking-internal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$(cat /run/secrets/postgres_user) -d $$(cat /run/secrets/postgres_db)"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- CHOWN
- DAC_OVERRIDE
- FOWNER
- SETGID
- SETUID
tmpfs:
- /tmp
- /var/run/postgresql
read_only: true
app:
build:
context: .
dockerfile: Dockerfile
target: production
container_name: appointment-booking-app
restart: unless-stopped
user: "1001:1001"
environment:
NODE_ENV: production
POSTGRES_DB_FILE: /run/secrets/postgres_db
POSTGRES_USER_FILE: /run/secrets/postgres_user
POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password
secrets:
- postgres_db
- postgres_user
- postgres_password
depends_on:
postgres:
condition: service_healthy
networks:
- appointment-booking-internal
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
tmpfs:
- /tmp
read_only: true
caddy:
image: caddy:2-alpine
container_name: appointment-booking-caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
networks:
- appointment-booking-internal
depends_on:
app:
condition: service_healthy
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
secrets:
postgres_db:
file: ./secrets/postgres_db.txt
postgres_user:
file: ./secrets/postgres_user.txt
postgres_password:
file: ./secrets/postgres_password.txt
volumes:
postgres_data:
driver: local
caddy_data:
driver: local
caddy_config:
driver: local
networks:
appointment-booking-internal:
driver: bridge
internal: false
+338
View File
@@ -0,0 +1,338 @@
# Infrastructure Setup
This document describes the complete setup for the appointment booking application infrastructure.
## Architecture Overview
The application consists of three Docker containers:
1. **PostgreSQL Database** - Data persistence
2. **SvelteKit Application** - Web application (production only)
3. **Caddy Reverse Proxy** - HTTPS termination and routing (production only)
## Development Setup
Development runs only the PostgreSQL database with a public port for development tools.
### Prerequisites
- Docker and Docker Compose
- Node.js 24+
- Git
### Setup Steps
1. **Clone the repository**
```bash
git clone <repository-url>
cd appointment-booking-software
```
2. **Configure environment**
```bash
cp .env.example .env
```
Edit `.env` and set secure values:
```
POSTGRES_DB=appointment_booking
POSTGRES_USER=postgres
POSTGRES_PASSWORD=your_secure_development_password
POSTGRES_PORT=5432
```
3. **Start development database**
```bash
npm run docker:dev:up
```
4. **Verify database connection**
```bash
# Check container status
docker ps
# Test connection
psql -h localhost -U postgres -d appointment_booking
```
5. **Install SvelteKit dependencies** (when SvelteKit code is added)
```bash
npm install
```
6. **Start SvelteKit development server** (when SvelteKit code is added)
```bash
npm run dev
```
### Development Commands
```bash
# Database management
npm run docker:dev:up # Start PostgreSQL
npm run docker:dev:down # Stop PostgreSQL
npm run docker:dev:logs # View database logs
npm run docker:dev:clean # Remove containers and volumes
# Application (when SvelteKit is implemented)
npm run dev # Start development server
npm run build # Build for production
```
## Production Setup
Production runs all three containers with security hardening and HTTPS.
### Prerequisites
- Docker and Docker Compose
- Domain name with DNS pointing to server
- Server with public IP
### Setup Steps
1. **Configure production secrets**
Edit the secret files in the `secrets/` directory:
```bash
# Database configuration
echo "appointment_booking" > secrets/postgres_db.txt
echo "postgres" > secrets/postgres_user.txt
echo "$(openssl rand -base64 32)" > secrets/postgres_password.txt
# Set proper permissions
chmod 600 secrets/*.txt
```
2. **Configure domain**
Edit `Caddyfile` and replace `your-domain.com` with your actual domain:
```
yourdomain.com {
# ... rest of configuration
}
```
3. **Build and start production stack**
```bash
# Build application image
npm run docker:prod:build
# Start all services
npm run docker:prod:up
```
4. **Verify deployment**
```bash
# Check all containers are running
docker ps
# Check application health
npm run docker:health
# View logs
npm run docker:prod:logs
```
### Production Commands
```bash
# Stack management
npm run docker:prod:build # Build application image
npm run docker:prod:up # Start all services
npm run docker:prod:down # Stop all services
npm run docker:prod:logs # View all logs
npm run docker:prod:clean # Remove containers and volumes
npm run docker:health # Check container status
npm run docker:tag-and-push # Build, tag and push the app image to GHCR (Remember to change username accordingly first)
```
## SvelteKit Application Setup
When implementing the SvelteKit application, follow these steps:
### Database Connection
Create `src/lib/database.js`:
```javascript
import { readFileSync } from 'fs';
import pg from 'pg';
function getDatabaseConfig() {
if (process.env.NODE_ENV === 'production') {
// Read from Docker secrets
const user = readFileSync('/run/secrets/postgres_user', 'utf8').trim();
const password = readFileSync('/run/secrets/postgres_password', 'utf8').trim();
const database = readFileSync('/run/secrets/postgres_db', 'utf8').trim();
return {
host: 'postgres',
port: 5432,
user,
password,
database
};
} else {
// Development configuration
return {
host: 'localhost',
port: process.env.POSTGRES_PORT || 5432,
user: process.env.POSTGRES_USER || 'postgres',
password: process.env.POSTGRES_PASSWORD,
database: process.env.POSTGRES_DB || 'appointment_booking'
};
}
}
export const pool = new pg.Pool(getDatabaseConfig());
```
### Health Check Endpoint
Create `src/routes/health/+server.js`:
```javascript
import { json } from '@sveltejs/kit';
import { pool } from '$lib/database.js';
export async function GET() {
try {
await pool.query('SELECT 1');
return json({ status: 'healthy', timestamp: new Date().toISOString() });
} catch (error) {
return json(
{ status: 'unhealthy', error: error.message },
{ status: 500 }
);
}
}
```
### SvelteKit Configuration
Update `vite.config.js`:
```javascript
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()],
server: {
host: '0.0.0.0',
port: 5173
}
});
```
## Security Features
### Container Security
- **Rootless containers**: All containers (sans caddy, which needs to access privileged ports) run as non-root users
- **Read-only filesystems**: Containers have read-only root filesystems where possible
- **No new privileges**: Containers cannot escalate privileges
- **Minimal capabilities**: Only necessary Linux capabilities are granted
### Network Security
- **Internal networks**: Database and app communicate via internal Docker network
- **No exposed ports**: Only Caddy exposes ports to the host
- **HTTPS only**: Caddy handles automatic HTTPS with Let's Encrypt
### Data Security
- **Docker secrets**: Sensitive data stored as Docker secrets
- **Encrypted connections**: Database connections use SSL/TLS
- **Security headers**: Comprehensive security headers via Caddy
## Monitoring and Logs
### Log Locations
- **Caddy logs**: Stored in `/data/access.log` inside Caddy container
- **Application logs**: Available via `docker logs`
- **Database logs**: Available via `docker logs`
### Health Checks
- **Application**: `GET /health` endpoint
- **Database**: Built-in PostgreSQL health check
- **Caddy**: HTTP response check
### Monitoring Commands
```bash
# View real-time logs
npm run docker:prod:logs
# Check container health
docker ps
npm run docker:health
# Individual container logs
docker logs appointment-booking-app
docker logs appointment-booking-postgres
docker logs appointment-booking-caddy
```
## Troubleshooting
### Common Issues
1. **Container won't start**
```bash
# Check container logs
docker logs <container-name>
# Check resource usage
docker stats
```
2. **Database connection issues**
```bash
# Test database connectivity
docker exec -it appointment-booking-postgres psql -U postgres -d appointment_booking
# Check database logs
docker logs appointment-booking-postgres
```
3. **HTTPS certificate issues**
```bash
# Check Caddy logs
docker logs appointment-booking-caddy
# Verify domain DNS
nslookup yourdomain.com
```
4. **Application not accessible**
```bash
# Check if all containers are running
docker ps
# Test internal connectivity
docker exec -it appointment-booking-caddy wget -q --spider http://app:3000/health
```
### Reset Commands
```bash
# Complete reset (destroys all data)
npm run docker:prod:clean
npm run docker:dev:clean
# Rebuild and restart
npm run docker:prod:build
npm run docker:prod:up
```
## Backup and Restore
### Database Backup
```bash
# Create backup
docker exec appointment-booking-postgres pg_dump -U postgres appointment_booking > backup.sql
# Restore backup
docker exec -i appointment-booking-postgres psql -U postgres appointment_booking < backup.sql
```
### Volume Backup
```bash
# Backup PostgreSQL data volume
docker run --rm -v appointment-booking-software_postgres_data:/data -v $(pwd):/backup alpine tar czf /backup/postgres-backup.tar.gz /data
```
+23
View File
@@ -0,0 +1,23 @@
{
"name": "appointment-booking-software",
"version": "0.0.1",
"description": "End-to-end encrypted appointment booking platform",
"type": "module",
"scripts": {
"build": "echo 'Build placeholder - will be replaced with SvelteKit build'",
"docker:dev:up": "docker compose -f docker-compose.dev.yml up -d",
"docker:dev:down": "docker compose -f docker-compose.dev.yml down",
"docker:dev:logs": "docker compose -f docker-compose.dev.yml logs -f",
"docker:dev:clean": "docker compose -f docker-compose.dev.yml down -v --remove-orphans",
"docker:prod:build": "docker compose -f docker-compose.prod.yml build --no-cache",
"docker:prod:up": "docker compose -f docker-compose.prod.yml up -d",
"docker:prod:down": "docker compose -f docker-compose.prod.yml down",
"docker:prod:logs": "docker compose -f docker-compose.prod.yml logs -f",
"docker:prod:clean": "docker compose -f docker-compose.prod.yml down -v --remove-orphans",
"docker:tag-and-push": "npm run docker:prod:build && docker tag appointment-booking-software-app:latest appointment-booking-software:$npm_package_version && docker push ghcr.io/username/appointment-booking-software:$npm_package_version"
},
"engines": {
"node": ">=24.0.0"
},
"license": "AGPL-3.0"
}
View File