Add complete Docker configuration for containerized deployment: - Multi-stage Dockerfile using official Bun runtime - docker-compose.yml for single-port stack orchestration - Host-mounted database volume with auto-initialization - Custom database init script using bun:sqlite - Entrypoint script for seamless database setup - Environment configuration template - Comprehensive DOCKER.md documentation Key features: - Single exposed port (3001) serving both API and frontend - Database persists in ./data directory on host - Auto-creates database from template on first startup - Health checks for monitoring - Architecture-agnostic (works on x86 and ARM64) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
220 lines
4.6 KiB
Markdown
220 lines
4.6 KiB
Markdown
# Docker Deployment Guide
|
|
|
|
This guide covers deploying Quickawards using Docker.
|
|
|
|
## Quick Start
|
|
|
|
1. **Create environment file:**
|
|
```bash
|
|
cp .env.docker.example .env
|
|
```
|
|
|
|
2. **Generate secure JWT secret:**
|
|
```bash
|
|
openssl rand -base64 32
|
|
```
|
|
Copy the output and set it as `JWT_SECRET` in `.env`.
|
|
|
|
3. **Update `.env` with your settings:**
|
|
- `JWT_SECRET`: Strong random string (required)
|
|
- `VITE_APP_URL`: Your domain (e.g., `https://awards.example.com`)
|
|
- `ALLOWED_ORIGINS`: Your domain(s) for CORS
|
|
|
|
4. **Start the application:**
|
|
```bash
|
|
docker-compose up -d
|
|
```
|
|
|
|
5. **Access the application:**
|
|
- URL: http://localhost:3001
|
|
- Health check: http://localhost:3001/api/health
|
|
|
|
## Architecture
|
|
|
|
### Single Port Design
|
|
|
|
The Docker stack exposes a single port (3001) which serves both:
|
|
- **Backend API** (`/api/*`)
|
|
- **Frontend SPA** (all other routes)
|
|
|
|
### Database Persistence
|
|
|
|
- **Location**: `./data/award.db` (host-mounted volume)
|
|
- **Initialization**: Automatic on first startup
|
|
- **Persistence**: Database survives container restarts/recreations
|
|
|
|
### Startup Behavior
|
|
|
|
1. **First startup**: Database is created from template
|
|
2. **Subsequent startups**: Existing database is used
|
|
3. **Container recreation**: Database persists in volume
|
|
|
|
## Commands
|
|
|
|
### Start the application
|
|
```bash
|
|
docker-compose up -d
|
|
```
|
|
|
|
### View logs
|
|
```bash
|
|
docker-compose logs -f
|
|
```
|
|
|
|
### Stop the application
|
|
```bash
|
|
docker-compose down
|
|
```
|
|
|
|
### Rebuild after code changes
|
|
```bash
|
|
docker-compose up -d --build
|
|
```
|
|
|
|
### Stop and remove everything (including database volume)
|
|
```bash
|
|
docker-compose down -v
|
|
```
|
|
|
|
## Environment Variables
|
|
|
|
| Variable | Required | Default | Description |
|
|
|----------|----------|---------|-------------|
|
|
| `NODE_ENV` | No | `production` | Environment mode |
|
|
| `PORT` | No | `3001` | Application port |
|
|
| `LOG_LEVEL` | No | `info` | Logging level (debug/info/warn/error) |
|
|
| `JWT_SECRET` | **Yes** | - | JWT signing secret (change this!) |
|
|
| `VITE_APP_URL` | No | - | Your application's public URL |
|
|
| `ALLOWED_ORIGINS` | No | - | CORS allowed origins (comma-separated) |
|
|
|
|
## Database Management
|
|
|
|
### Backup the database
|
|
```bash
|
|
cp data/award.db data/award.db.backup.$(date +%Y%m%d)
|
|
```
|
|
|
|
### Restore from backup
|
|
```bash
|
|
docker-compose down
|
|
cp data/award.db.backup.YYYYMMDD data/award.db
|
|
docker-compose up -d
|
|
```
|
|
|
|
### Reset the database
|
|
```bash
|
|
docker-compose down -v
|
|
docker-compose up -d
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Container won't start
|
|
```bash
|
|
# Check logs
|
|
docker-compose logs -f
|
|
|
|
# Check container status
|
|
docker-compose ps
|
|
```
|
|
|
|
### Database errors
|
|
```bash
|
|
# Check database file exists
|
|
ls -la data/
|
|
|
|
# Check database permissions
|
|
stat data/award.db
|
|
```
|
|
|
|
### Port already in use
|
|
Change the port mapping in `docker-compose.yml`:
|
|
```yaml
|
|
ports:
|
|
- "8080:3001" # Maps host port 8080 to container port 3001
|
|
```
|
|
|
|
### Health check failing
|
|
```bash
|
|
# Check if container is responding
|
|
curl http://localhost:3001/api/health
|
|
|
|
# Check container logs
|
|
docker-compose logs quickawards
|
|
```
|
|
|
|
## Production Deployment
|
|
|
|
### Using a Reverse Proxy (nginx)
|
|
|
|
Example nginx configuration:
|
|
|
|
```nginx
|
|
server {
|
|
listen 80;
|
|
server_name awards.example.com;
|
|
|
|
location / {
|
|
proxy_pass http://localhost:3001;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection 'upgrade';
|
|
proxy_set_header Host $host;
|
|
proxy_cache_bypass $http_upgrade;
|
|
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;
|
|
}
|
|
}
|
|
```
|
|
|
|
### SSL/TLS with Let's Encrypt
|
|
|
|
Use certbot with nginx:
|
|
|
|
```bash
|
|
sudo certbot --nginx -d awards.example.com
|
|
```
|
|
|
|
### Security Checklist
|
|
|
|
- [ ] Set strong `JWT_SECRET`
|
|
- [ ] Set `NODE_ENV=production`
|
|
- [ ] Set `LOG_LEVEL=info` (or `warn` in production)
|
|
- [ ] Configure `ALLOWED_ORIGINS` to your domain only
|
|
- [ ] Use HTTPS/TLS in production
|
|
- [ ] Regular database backups
|
|
- [ ] Monitor logs for suspicious activity
|
|
- [ ] Keep Docker image updated
|
|
|
|
## File Structure After Deployment
|
|
|
|
```
|
|
project/
|
|
├── data/
|
|
│ └── award.db # Persisted database (volume mount)
|
|
├── docker-compose.yml
|
|
├── Dockerfile
|
|
├── .dockerignore
|
|
├── .env # Your environment variables
|
|
└── ... (source code)
|
|
```
|
|
|
|
## Building Without docker-compose
|
|
|
|
If you prefer to use `docker` directly:
|
|
|
|
```bash
|
|
# Build the image
|
|
docker build -t quickawards .
|
|
|
|
# Run the container
|
|
docker run -d \
|
|
--name quickawards \
|
|
-p 3001:3001 \
|
|
-v $(pwd)/data:/data \
|
|
-e JWT_SECRET=your-secret-here \
|
|
-e NODE_ENV=production \
|
|
quickawards
|
|
```
|