Compare commits
2 commits
main
...
feature/2-
| Author | SHA1 | Date | |
|---|---|---|---|
| fd05ffd06d | |||
| ed27f4bb49 |
11 changed files with 1068 additions and 11 deletions
109
.docker/development/README.md
Normal file
109
.docker/development/README.md
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
# Development Docker Setup
|
||||
|
||||
This directory contains Docker Compose files for local development using Laravel Sail.
|
||||
|
||||
## Files
|
||||
|
||||
- **docker-compose.yml** - Base Sail configuration for backend (Laravel) and MySQL
|
||||
- **docker-compose.override.yml** - Development overrides:
|
||||
- Podman/SELinux compatibility (`:Z` volume flags)
|
||||
- Standard MySQL image (instead of mysql-server)
|
||||
- Frontend container (Node.js development server)
|
||||
|
||||
## Quick Start
|
||||
|
||||
Use the automated setup script from the project root:
|
||||
|
||||
```bash
|
||||
./bin/start-dev
|
||||
```
|
||||
|
||||
This script will:
|
||||
1. Create `.env` if it doesn't exist
|
||||
2. Install backend dependencies (composer)
|
||||
3. Start all containers (backend, MySQL, frontend)
|
||||
4. Run database migrations
|
||||
5. Optionally seed the database
|
||||
|
||||
## Manual Usage
|
||||
|
||||
### Start all services
|
||||
|
||||
```bash
|
||||
docker compose -f .docker/development/docker-compose.yml \
|
||||
-f .docker/development/docker-compose.override.yml \
|
||||
up -d
|
||||
```
|
||||
|
||||
### View logs
|
||||
|
||||
```bash
|
||||
# All services
|
||||
docker compose -f .docker/development/docker-compose.yml \
|
||||
-f .docker/development/docker-compose.override.yml \
|
||||
logs -f
|
||||
|
||||
# Specific service
|
||||
docker compose -f .docker/development/docker-compose.yml \
|
||||
-f .docker/development/docker-compose.override.yml \
|
||||
logs -f frontend
|
||||
```
|
||||
|
||||
### Run backend commands
|
||||
|
||||
```bash
|
||||
# Run migrations
|
||||
docker compose -f .docker/development/docker-compose.yml \
|
||||
-f .docker/development/docker-compose.override.yml \
|
||||
exec backend php artisan migrate
|
||||
|
||||
# Run tinker
|
||||
docker compose -f .docker/development/docker-compose.yml \
|
||||
-f .docker/development/docker-compose.override.yml \
|
||||
exec backend php artisan tinker
|
||||
|
||||
# Run tests
|
||||
docker compose -f .docker/development/docker-compose.yml \
|
||||
-f .docker/development/docker-compose.override.yml \
|
||||
exec backend php artisan test
|
||||
```
|
||||
|
||||
### Stop all services
|
||||
|
||||
```bash
|
||||
docker compose -f .docker/development/docker-compose.yml \
|
||||
-f .docker/development/docker-compose.override.yml \
|
||||
down
|
||||
```
|
||||
|
||||
## Services
|
||||
|
||||
| Service | Port | Description |
|
||||
|---------|------|-------------|
|
||||
| **backend** | 8000 | Laravel API (PHP 8.4 + Sail) |
|
||||
| **mysql** | 3306 | MySQL 8.0 database |
|
||||
| **frontend** | 5173 | Vite dev server (React Router) |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
The backend reads from `backend/.env`. Key variables:
|
||||
|
||||
- `APP_PORT` - Backend port (default: 80, set to 8000 for Podman)
|
||||
- `DB_*` - Database connection settings
|
||||
- `FORWARD_DB_PORT` - Exposed MySQL port (default: 3306)
|
||||
|
||||
## Volume Mounts
|
||||
|
||||
- `backend/` → `/var/www/html` (backend container)
|
||||
- `frontend/` → `/app` (frontend container)
|
||||
- MySQL data persists in named volume `sail-mysql`
|
||||
|
||||
## Podman Compatibility
|
||||
|
||||
The `:Z` flag on volumes enables SELinux compatibility for Podman users. This is automatically included in the override file.
|
||||
|
||||
## Notes
|
||||
|
||||
- The frontend container automatically runs `npm install` and `npm run dev` on startup
|
||||
- Laravel Sail handles PHP-FPM, supervisor, and other backend services
|
||||
- Hot reload is enabled for both frontend (Vite HMR) and backend (file watchers)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
services:
|
||||
backend:
|
||||
volumes:
|
||||
- '.:/var/www/html:Z'
|
||||
- '../../backend:/var/www/html:Z'
|
||||
|
||||
mysql:
|
||||
image: 'docker.io/library/mysql:8.0'
|
||||
|
|
@ -16,7 +16,7 @@ services:
|
|||
working_dir: /app
|
||||
command: sh -c "npm install && npm run dev -- --host"
|
||||
volumes:
|
||||
- '../frontend:/app:Z'
|
||||
- '../../frontend:/app:Z'
|
||||
ports:
|
||||
- '5173:5173'
|
||||
networks:
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
services:
|
||||
backend:
|
||||
build:
|
||||
context: './vendor/laravel/sail/runtimes/8.4'
|
||||
context: '../../backend/vendor/laravel/sail/runtimes/8.4'
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
WWWGROUP: '${WWWGROUP}'
|
||||
|
|
@ -18,7 +18,7 @@ services:
|
|||
XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}'
|
||||
IGNITION_LOCAL_SITES_PATH: '${PWD}'
|
||||
volumes:
|
||||
- '.:/var/www/html:Z'
|
||||
- '../../backend:/var/www/html:Z'
|
||||
networks:
|
||||
- sail
|
||||
depends_on:
|
||||
29
.docker/production/.dockerignore
Normal file
29
.docker/production/.dockerignore
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Ignore .env files - they should be created at runtime
|
||||
backend/.env
|
||||
backend/.env.production
|
||||
|
||||
# Ignore node_modules - we install dependencies during build
|
||||
frontend/node_modules
|
||||
backend/vendor
|
||||
|
||||
# Ignore git
|
||||
.git
|
||||
.gitignore
|
||||
|
||||
# Ignore build artifacts
|
||||
frontend/build
|
||||
backend/bootstrap/cache/*
|
||||
backend/storage/logs/*
|
||||
backend/storage/framework/cache/*
|
||||
backend/storage/framework/sessions/*
|
||||
backend/storage/framework/views/*
|
||||
|
||||
# Ignore test files
|
||||
backend/tests
|
||||
frontend/tests
|
||||
|
||||
# Ignore development files
|
||||
.vscode
|
||||
.idea
|
||||
*.log
|
||||
.DS_Store
|
||||
151
.docker/production/Dockerfile
Normal file
151
.docker/production/Dockerfile
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
# ============================================
|
||||
# Dish Planner - All-in-One Production Image
|
||||
# ============================================
|
||||
# This Dockerfile creates a single container with:
|
||||
# - MySQL 8.0 database
|
||||
# - PHP 8.2-FPM (Laravel backend)
|
||||
# - Node.js 20 (React Router frontend)
|
||||
# - Nginx (reverse proxy)
|
||||
# - Supervisor (process manager)
|
||||
#
|
||||
# Build: docker build -f .docker/production/Dockerfile -t codeberg.org/lvl0/dish-planner:latest .
|
||||
# Deploy: See .docker/production/README.md for deployment instructions
|
||||
# ============================================
|
||||
|
||||
FROM ubuntu:22.04
|
||||
|
||||
# Prevent interactive prompts during build
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV TZ=UTC
|
||||
|
||||
# ============================================
|
||||
# Install System Dependencies
|
||||
# ============================================
|
||||
RUN apt-get update && apt-get install -y \
|
||||
# Basic utilities
|
||||
curl \
|
||||
wget \
|
||||
git \
|
||||
unzip \
|
||||
ca-certificates \
|
||||
gnupg \
|
||||
supervisor \
|
||||
software-properties-common \
|
||||
# Nginx
|
||||
nginx \
|
||||
# MySQL Server
|
||||
mysql-server \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# ============================================
|
||||
# Install PHP 8.2 from ondrej/php PPA
|
||||
# ============================================
|
||||
RUN add-apt-repository ppa:ondrej/php -y \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y \
|
||||
php8.2-fpm \
|
||||
php8.2-cli \
|
||||
php8.2-mysql \
|
||||
php8.2-mbstring \
|
||||
php8.2-xml \
|
||||
php8.2-bcmath \
|
||||
php8.2-curl \
|
||||
php8.2-zip \
|
||||
php8.2-gd \
|
||||
php8.2-intl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# ============================================
|
||||
# Install Node.js 20
|
||||
# ============================================
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
|
||||
&& apt-get install -y nodejs \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# ============================================
|
||||
# Install Composer
|
||||
# ============================================
|
||||
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
|
||||
|
||||
# ============================================
|
||||
# Set up working directories
|
||||
# ============================================
|
||||
WORKDIR /var/www
|
||||
|
||||
# Create necessary directories
|
||||
RUN mkdir -p /var/www/backend \
|
||||
/var/www/frontend \
|
||||
/var/www/storage/logs \
|
||||
/var/lib/mysql \
|
||||
/run/php
|
||||
|
||||
# ============================================
|
||||
# Copy and Build Backend (Laravel)
|
||||
# ============================================
|
||||
COPY backend /var/www/backend
|
||||
WORKDIR /var/www/backend
|
||||
|
||||
# Remove any existing .env files - they will be created at runtime
|
||||
RUN rm -f .env .env.production
|
||||
|
||||
# Install PHP dependencies (production)
|
||||
RUN composer install --no-dev --optimize-autoloader --no-interaction
|
||||
|
||||
# Set permissions for Laravel
|
||||
RUN chown -R www-data:www-data /var/www/backend/storage /var/www/backend/bootstrap/cache \
|
||||
&& chmod -R 775 /var/www/backend/storage /var/www/backend/bootstrap/cache
|
||||
|
||||
# Add www-data to mysql group so it can access MySQL socket
|
||||
RUN usermod -a -G mysql www-data
|
||||
|
||||
# ============================================
|
||||
# Copy and Build Frontend (React Router)
|
||||
# ============================================
|
||||
COPY frontend /var/www/frontend
|
||||
WORKDIR /var/www/frontend
|
||||
|
||||
# Install all dependencies (including dev for build), build, then remove dev deps
|
||||
RUN npm ci \
|
||||
&& npm run build \
|
||||
&& npm prune --production
|
||||
|
||||
# ============================================
|
||||
# Configure Nginx
|
||||
# ============================================
|
||||
COPY .docker/production/nginx.conf /etc/nginx/sites-available/default
|
||||
RUN ln -sf /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default \
|
||||
&& rm -f /etc/nginx/sites-enabled/default.old
|
||||
|
||||
# ============================================
|
||||
# Configure PHP-FPM
|
||||
# ============================================
|
||||
RUN sed -i 's/listen = \/run\/php\/php8.2-fpm.sock/listen = 127.0.0.1:9000/' /etc/php/8.2/fpm/pool.d/www.conf
|
||||
|
||||
# ============================================
|
||||
# Configure MySQL
|
||||
# ============================================
|
||||
# Allow MySQL to bind to all interfaces (for easier debugging if needed)
|
||||
RUN sed -i 's/bind-address.*/bind-address = 127.0.0.1/' /etc/mysql/mysql.conf.d/mysqld.cnf || true
|
||||
|
||||
# ============================================
|
||||
# Copy Configuration Files
|
||||
# ============================================
|
||||
COPY .docker/production/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||
COPY .docker/production/entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||
RUN chmod +x /usr/local/bin/entrypoint.sh
|
||||
|
||||
# ============================================
|
||||
# Create volume mount points
|
||||
# ============================================
|
||||
VOLUME ["/var/lib/mysql", "/var/www/backend/storage"]
|
||||
|
||||
# ============================================
|
||||
# Expose HTTP port
|
||||
# ============================================
|
||||
EXPOSE 80
|
||||
|
||||
# ============================================
|
||||
# Set entrypoint and default command
|
||||
# ============================================
|
||||
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|
||||
345
.docker/production/README.md
Normal file
345
.docker/production/README.md
Normal file
|
|
@ -0,0 +1,345 @@
|
|||
# Dish Planner - Self-Hosted Deployment Guide
|
||||
|
||||
This directory contains everything you need to deploy Dish Planner as a self-hosted all-in-one container.
|
||||
|
||||
> **Note**: This guide uses an embedded docker-compose configuration (no separate file to download). Simply copy the YAML configuration shown below and save it as `docker-compose.yml`.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### For End Users (Using Pre-Built Image)
|
||||
|
||||
> **Image Versioning**: This guide uses the `:latest` tag for simplicity. For production deployments, consider using a specific version tag (e.g., `:v0.3`) for stability and predictable updates.
|
||||
|
||||
1. **Create a docker-compose.yml file:**
|
||||
|
||||
Copy the following configuration and save it as `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
# ============================================
|
||||
# Dish Planner - Self-Hosted Deployment
|
||||
# ============================================
|
||||
#
|
||||
# QUICK START:
|
||||
# 1. Save this file as docker-compose.yml
|
||||
# 2. (Optional) Edit the environment variables below
|
||||
# 3. Run: docker compose up -d
|
||||
# 4. Access your app at http://localhost:3000
|
||||
#
|
||||
# IMPORTANT SECURITY NOTES:
|
||||
# - Database credentials are auto-generated on first run
|
||||
# - Check the container logs to see generated credentials:
|
||||
# docker logs dishplanner
|
||||
# - For production, set a custom APP_URL environment variable
|
||||
#
|
||||
# CUSTOMIZATION:
|
||||
# You can override any environment variable by uncommenting
|
||||
# and editing the values below, or by creating a .env file.
|
||||
#
|
||||
# ============================================
|
||||
|
||||
services:
|
||||
dishplanner:
|
||||
# Pre-built all-in-one image from Codeberg Container Registry
|
||||
image: codeberg.org/lvl0/dish-planner:latest
|
||||
|
||||
container_name: dishplanner
|
||||
restart: unless-stopped
|
||||
|
||||
# ----------------------------------------
|
||||
# Port Configuration
|
||||
# ----------------------------------------
|
||||
# The application will be accessible on port 3000
|
||||
# Change the left number to use a different host port
|
||||
# Example: "8080:80" to access on port 8080
|
||||
ports:
|
||||
- "3000:80"
|
||||
|
||||
# ----------------------------------------
|
||||
# Environment Variables
|
||||
# ----------------------------------------
|
||||
# Uncomment and customize as needed
|
||||
environment:
|
||||
# Application URL (set this to your domain)
|
||||
- APP_URL=http://localhost:3000
|
||||
|
||||
# Application environment (production recommended)
|
||||
- APP_ENV=production
|
||||
|
||||
# Debug mode (set to false in production for security)
|
||||
- APP_DEBUG=false
|
||||
|
||||
# Database configuration
|
||||
# Note: Credentials are auto-generated on first run if not set
|
||||
# Check logs for generated credentials: docker logs dishplanner
|
||||
# - DB_DATABASE=dishplanner
|
||||
# - DB_USERNAME=dishuser
|
||||
# - DB_PASSWORD=change-this-secure-password
|
||||
|
||||
# Timezone (optional)
|
||||
# - APP_TIMEZONE=UTC
|
||||
|
||||
# ----------------------------------------
|
||||
# Persistent Data Volumes
|
||||
# ----------------------------------------
|
||||
# These volumes ensure data persists across container restarts
|
||||
volumes:
|
||||
# MySQL database data
|
||||
- dishplanner_mysql_data:/var/lib/mysql
|
||||
|
||||
# Laravel storage (uploaded files, logs, cache)
|
||||
- dishplanner_storage:/var/www/backend/storage
|
||||
|
||||
# Supervisor logs
|
||||
- dishplanner_logs:/var/log/supervisor
|
||||
|
||||
# ----------------------------------------
|
||||
# Health Check (optional)
|
||||
# ----------------------------------------
|
||||
# Uncomment to enable container health monitoring
|
||||
# healthcheck:
|
||||
# test: ["CMD", "curl", "-f", "http://localhost/api/health"]
|
||||
# interval: 30s
|
||||
# timeout: 10s
|
||||
# retries: 3
|
||||
# start_period: 60s
|
||||
|
||||
# ----------------------------------------
|
||||
# Named Volumes
|
||||
# ----------------------------------------
|
||||
# Docker manages these volumes - data persists even if container is removed
|
||||
volumes:
|
||||
dishplanner_mysql_data:
|
||||
driver: local
|
||||
dishplanner_storage:
|
||||
driver: local
|
||||
dishplanner_logs:
|
||||
driver: local
|
||||
|
||||
# ----------------------------------------
|
||||
# Network Configuration
|
||||
# ----------------------------------------
|
||||
# Uses default bridge network (suitable for single-host deployment)
|
||||
# For advanced setups, you can define custom networks here
|
||||
```
|
||||
|
||||
2. **Start the application:**
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
3. **View the initialization logs to get your database credentials:**
|
||||
```bash
|
||||
docker logs dishplanner
|
||||
```
|
||||
|
||||
Save the auto-generated database credentials shown in the logs!
|
||||
|
||||
4. **Access the application:**
|
||||
Open your browser to `http://localhost:3000`
|
||||
|
||||
That's it! The application is now running with:
|
||||
- MySQL database
|
||||
- Laravel backend API
|
||||
- React Router frontend
|
||||
- Nginx reverse proxy
|
||||
- Background queue worker
|
||||
|
||||
### For DockGE Users
|
||||
|
||||
1. In the DockGE web UI, click "Add Stack"
|
||||
2. Copy the entire YAML configuration from the code block above (starting from `services:` and including all volumes)
|
||||
3. Paste it into the DockGE compose editor
|
||||
4. Optionally edit the environment variables
|
||||
5. Click "Start"
|
||||
6. View logs to get auto-generated database credentials
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
You can customize the deployment by setting these environment variables in the docker-compose file:
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `APP_URL` | `http://localhost:3000` | The URL where your app is accessible |
|
||||
| `APP_ENV` | `production` | Environment mode (production/local) |
|
||||
| `APP_DEBUG` | `false` | Enable debug mode (never use in production!) |
|
||||
| `DB_DATABASE` | `dishplanner` | Database name |
|
||||
| `DB_USERNAME` | `dishuser` | Database username |
|
||||
| `DB_PASSWORD` | Auto-generated | Database password (check logs for generated value) |
|
||||
|
||||
### Changing the Port
|
||||
|
||||
By default, the app runs on port 3000. To use a different port, edit the `ports` section:
|
||||
|
||||
```yaml
|
||||
ports:
|
||||
- "8080:80" # This makes the app available on port 8080
|
||||
```
|
||||
|
||||
### Persistent Data
|
||||
|
||||
The following data is persisted in Docker volumes:
|
||||
- **MySQL database** - All your dishes, schedules, and users
|
||||
- **Laravel storage** - Uploaded files, logs, and cache
|
||||
- **Supervisor logs** - Application and service logs
|
||||
|
||||
Even if you remove the container, this data remains intact.
|
||||
|
||||
## Building the Image Yourself
|
||||
|
||||
If you prefer to build the image yourself instead of using the pre-built one:
|
||||
|
||||
1. **Clone the repository:**
|
||||
```bash
|
||||
git clone https://codeberg.org/lvl0/dish-planner.git
|
||||
cd dish-planner
|
||||
```
|
||||
|
||||
2. **Build the image:**
|
||||
```bash
|
||||
docker build -f .docker/production/Dockerfile -t codeberg.org/lvl0/dish-planner:latest .
|
||||
```
|
||||
|
||||
3. **Create a docker-compose.yml file** using the configuration shown in the Quick Start section above (or save the embedded compose config to a file)
|
||||
|
||||
4. **Run it:**
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## Management
|
||||
|
||||
### Viewing Logs
|
||||
|
||||
```bash
|
||||
# All logs
|
||||
docker logs dishplanner
|
||||
|
||||
# Follow logs in real-time
|
||||
docker logs -f dishplanner
|
||||
|
||||
# Last 100 lines
|
||||
docker logs --tail 100 dishplanner
|
||||
```
|
||||
|
||||
### Stopping the Application
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
|
||||
### Restarting the Application
|
||||
|
||||
```bash
|
||||
docker compose restart
|
||||
```
|
||||
|
||||
### Updating to a New Version
|
||||
|
||||
1. Pull the latest image:
|
||||
```bash
|
||||
docker compose pull
|
||||
```
|
||||
|
||||
2. Recreate the container:
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
3. Your data persists in volumes automatically!
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Cannot connect to database"
|
||||
|
||||
Check that MySQL started successfully:
|
||||
```bash
|
||||
docker logs dishplanner | grep mysql
|
||||
```
|
||||
|
||||
### "502 Bad Gateway"
|
||||
|
||||
One of the services may not have started. Check supervisor logs:
|
||||
```bash
|
||||
docker exec dishplanner supervisorctl status
|
||||
```
|
||||
|
||||
### Reset Everything (CAUTION: Deletes all data!)
|
||||
|
||||
```bash
|
||||
docker compose down -v # The -v flag removes volumes
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Access the Container Shell
|
||||
|
||||
```bash
|
||||
docker exec -it dishplanner bash
|
||||
```
|
||||
|
||||
### Run Laravel Commands
|
||||
|
||||
```bash
|
||||
# Run migrations
|
||||
docker exec dishplanner php /var/www/backend/artisan migrate
|
||||
|
||||
# Create a new user (if needed)
|
||||
docker exec -it dishplanner php /var/www/backend/artisan tinker
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
This all-in-one container includes:
|
||||
|
||||
- **MySQL 8.0** - Database server
|
||||
- **PHP 8.2-FPM** - Runs the Laravel backend
|
||||
- **Node.js 20** - Runs the React Router frontend
|
||||
- **Nginx** - Web server and reverse proxy
|
||||
- **Supervisord** - Process manager that keeps everything running
|
||||
|
||||
All services start automatically and are monitored by supervisord. If any service crashes, it will be automatically restarted.
|
||||
|
||||
## Security Recommendations
|
||||
|
||||
For production deployments:
|
||||
|
||||
1. **Set a strong database password:**
|
||||
```yaml
|
||||
environment:
|
||||
- DB_PASSWORD=your-very-secure-password-here
|
||||
```
|
||||
|
||||
2. **Use HTTPS:** Put the container behind a reverse proxy (Nginx, Caddy, Traefik) with SSL/TLS
|
||||
|
||||
3. **Set APP_DEBUG to false:**
|
||||
```yaml
|
||||
environment:
|
||||
- APP_DEBUG=false
|
||||
```
|
||||
|
||||
4. **Keep the image updated:** Regularly pull and deploy new versions
|
||||
|
||||
5. **Backup your data:**
|
||||
```bash
|
||||
# Backup volumes
|
||||
docker run --rm -v dishplanner_mysql_data:/data -v $(pwd):/backup ubuntu tar czf /backup/mysql-backup.tar.gz /data
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
For issues and questions:
|
||||
- Codeberg Issues: https://codeberg.org/lvl0/dish-planner/issues
|
||||
- Documentation: https://codeberg.org/lvl0/dish-planner
|
||||
|
||||
## What's Inside
|
||||
|
||||
The container runs these processes (managed by supervisord):
|
||||
|
||||
1. **MySQL** - Database (port 3306, internal only)
|
||||
2. **PHP-FPM** - Laravel application (port 9000, internal only)
|
||||
3. **Node.js** - React frontend (port 3000, internal only)
|
||||
4. **Nginx** - Reverse proxy (port 80, exposed)
|
||||
5. **Queue Worker** - Background job processor
|
||||
|
||||
Only Nginx's port 80 is exposed to the host (mapped to 3000 by default).
|
||||
105
.docker/production/docker-compose.test.yml
Normal file
105
.docker/production/docker-compose.test.yml
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
# ============================================
|
||||
# Dish Planner - Self-Hosted Deployment
|
||||
# ============================================
|
||||
#
|
||||
# QUICK START:
|
||||
# 1. Copy this file to your server
|
||||
# 2. (Optional) Edit the environment variables below
|
||||
# 3. Run: docker compose up -d
|
||||
# 4. Access your app at http://localhost:3000
|
||||
#
|
||||
# IMPORTANT SECURITY NOTES:
|
||||
# - Database credentials are auto-generated on first run
|
||||
# - Check the container logs to see generated credentials:
|
||||
# docker logs dishplanner
|
||||
# - For production, set a custom APP_URL environment variable
|
||||
#
|
||||
# CUSTOMIZATION:
|
||||
# You can override any environment variable by uncommenting
|
||||
# and editing the values below, or by creating a .env file.
|
||||
#
|
||||
# ============================================
|
||||
|
||||
services:
|
||||
dishplanner:
|
||||
# Pre-built all-in-one image from Docker Hub
|
||||
image: localhost/dishplanner-allinone:test
|
||||
|
||||
container_name: dishplanner
|
||||
restart: unless-stopped
|
||||
|
||||
# ----------------------------------------
|
||||
# Port Configuration
|
||||
# ----------------------------------------
|
||||
# The application will be accessible on port 3000
|
||||
# Change the left number to use a different host port
|
||||
# Example: "8080:80" to access on port 8080
|
||||
ports:
|
||||
- "3000:80"
|
||||
|
||||
# ----------------------------------------
|
||||
# Environment Variables
|
||||
# ----------------------------------------
|
||||
# Uncomment and customize as needed
|
||||
environment:
|
||||
# Application URL (set this to your domain)
|
||||
- APP_URL=http://localhost:3000
|
||||
|
||||
# Application environment (production recommended)
|
||||
- APP_ENV=production
|
||||
|
||||
# Debug mode (set to false in production for security)
|
||||
- APP_DEBUG=false
|
||||
|
||||
# Database configuration
|
||||
# Note: Credentials are auto-generated on first run if not set
|
||||
# Check logs for generated credentials: docker logs dishplanner
|
||||
# - DB_DATABASE=dishplanner
|
||||
# - DB_USERNAME=dishuser
|
||||
# - DB_PASSWORD=change-this-secure-password
|
||||
|
||||
# Timezone (optional)
|
||||
# - APP_TIMEZONE=UTC
|
||||
|
||||
# ----------------------------------------
|
||||
# Persistent Data Volumes
|
||||
# ----------------------------------------
|
||||
# These volumes ensure data persists across container restarts
|
||||
volumes:
|
||||
# MySQL database data
|
||||
- dishplanner_mysql_data:/var/lib/mysql
|
||||
|
||||
# Laravel storage (uploaded files, logs, cache)
|
||||
- dishplanner_storage:/var/www/backend/storage
|
||||
|
||||
# Supervisor logs
|
||||
- dishplanner_logs:/var/log/supervisor
|
||||
|
||||
# ----------------------------------------
|
||||
# Health Check (optional)
|
||||
# ----------------------------------------
|
||||
# Uncomment to enable container health monitoring
|
||||
# healthcheck:
|
||||
# test: ["CMD", "curl", "-f", "http://localhost/api/health"]
|
||||
# interval: 30s
|
||||
# timeout: 10s
|
||||
# retries: 3
|
||||
# start_period: 60s
|
||||
|
||||
# ----------------------------------------
|
||||
# Named Volumes
|
||||
# ----------------------------------------
|
||||
# Docker manages these volumes - data persists even if container is removed
|
||||
volumes:
|
||||
dishplanner_mysql_data:
|
||||
driver: local
|
||||
dishplanner_storage:
|
||||
driver: local
|
||||
dishplanner_logs:
|
||||
driver: local
|
||||
|
||||
# ----------------------------------------
|
||||
# Network Configuration
|
||||
# ----------------------------------------
|
||||
# Uses default bridge network (suitable for single-host deployment)
|
||||
# For advanced setups, you can define custom networks here
|
||||
153
.docker/production/entrypoint.sh
Normal file
153
.docker/production/entrypoint.sh
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "=========================================="
|
||||
echo "Dish Planner - Starting Initialization"
|
||||
echo "=========================================="
|
||||
|
||||
# ============================================
|
||||
# MySQL Initialization
|
||||
# ============================================
|
||||
echo "[1/6] Initializing MySQL..."
|
||||
|
||||
# Check if MySQL data directory is empty (first run)
|
||||
if [ ! -d "/var/lib/mysql/mysql" ]; then
|
||||
echo " → First run detected, initializing MySQL data directory..."
|
||||
mysqld --initialize-insecure --user=mysql --datadir=/var/lib/mysql
|
||||
fi
|
||||
|
||||
# Start MySQL temporarily in background for setup
|
||||
echo " → Starting MySQL..."
|
||||
mysqld --user=mysql --datadir=/var/lib/mysql &
|
||||
MYSQL_PID=$!
|
||||
|
||||
# Wait for MySQL to be ready
|
||||
echo " → Waiting for MySQL to be ready..."
|
||||
for i in {1..30}; do
|
||||
if mysqladmin ping -h localhost --silent; then
|
||||
echo " → MySQL is ready!"
|
||||
break
|
||||
fi
|
||||
echo " → Waiting... ($i/30)"
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# ============================================
|
||||
# Database Setup
|
||||
# ============================================
|
||||
echo "[2/6] Setting up database..."
|
||||
|
||||
# Set default values for database credentials
|
||||
DB_DATABASE=${DB_DATABASE:-dishplanner}
|
||||
DB_USERNAME=${DB_USERNAME:-dishuser}
|
||||
|
||||
# Check if this is first run by looking for credentials file
|
||||
CREDS_FILE="/var/www/backend/storage/.db_credentials"
|
||||
|
||||
if [ -f "$CREDS_FILE" ]; then
|
||||
# Not first run - load existing credentials
|
||||
echo " → Loading existing database credentials..."
|
||||
source "$CREDS_FILE"
|
||||
else
|
||||
# First run - generate new credentials and save them
|
||||
echo " → First run detected, generating credentials..."
|
||||
DB_PASSWORD=${DB_PASSWORD:-$(openssl rand -base64 32)}
|
||||
MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-$(openssl rand -base64 32)}
|
||||
|
||||
# Save credentials for future restarts
|
||||
cat > "$CREDS_FILE" <<EOF
|
||||
DB_PASSWORD='${DB_PASSWORD}'
|
||||
MYSQL_ROOT_PASSWORD='${MYSQL_ROOT_PASSWORD}'
|
||||
EOF
|
||||
chmod 600 "$CREDS_FILE"
|
||||
|
||||
# Set root password
|
||||
mysql -u root <<-EOSQL
|
||||
ALTER USER 'root'@'localhost' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}';
|
||||
FLUSH PRIVILEGES;
|
||||
EOSQL
|
||||
|
||||
# Create database and user
|
||||
mysql -u root -p"${MYSQL_ROOT_PASSWORD}" <<-EOSQL
|
||||
CREATE DATABASE IF NOT EXISTS ${DB_DATABASE};
|
||||
CREATE USER IF NOT EXISTS '${DB_USERNAME}'@'localhost' IDENTIFIED BY '${DB_PASSWORD}';
|
||||
CREATE USER IF NOT EXISTS '${DB_USERNAME}'@'%' IDENTIFIED BY '${DB_PASSWORD}';
|
||||
GRANT ALL PRIVILEGES ON ${DB_DATABASE}.* TO '${DB_USERNAME}'@'localhost';
|
||||
GRANT ALL PRIVILEGES ON ${DB_DATABASE}.* TO '${DB_USERNAME}'@'%';
|
||||
FLUSH PRIVILEGES;
|
||||
EOSQL
|
||||
|
||||
echo " → Database '${DB_DATABASE}' created"
|
||||
echo " → User '${DB_USERNAME}' created"
|
||||
fi
|
||||
|
||||
# ============================================
|
||||
# Laravel Environment Setup
|
||||
# ============================================
|
||||
echo "[3/6] Configuring Laravel environment..."
|
||||
|
||||
cd /var/www/backend
|
||||
|
||||
# Create .env if it doesn't exist
|
||||
if [ ! -f .env ]; then
|
||||
echo " → Creating .env file..."
|
||||
cp .env.example .env
|
||||
fi
|
||||
|
||||
# Update database credentials in .env using a more robust method
|
||||
# Use @ as delimiter to avoid conflicts with special chars in passwords
|
||||
sed -i "s@DB_DATABASE=.*@DB_DATABASE=${DB_DATABASE}@" .env
|
||||
sed -i "s@DB_USERNAME=.*@DB_USERNAME=${DB_USERNAME}@" .env
|
||||
sed -i "s@DB_PASSWORD=.*@DB_PASSWORD=${DB_PASSWORD}@" .env
|
||||
sed -i "s@DB_HOST=.*@DB_HOST=127.0.0.1@" .env
|
||||
sed -i "s@DB_PORT=.*@DB_PORT=3306@" .env
|
||||
|
||||
# Generate APP_KEY if not set
|
||||
if ! grep -q "APP_KEY=base64:" .env; then
|
||||
echo " → Generating application key..."
|
||||
php artisan key:generate --force
|
||||
else
|
||||
echo " → Application key already set"
|
||||
fi
|
||||
|
||||
# Set APP_URL if provided
|
||||
if [ -n "${APP_URL}" ]; then
|
||||
sed -i "s@APP_URL=.*@APP_URL=${APP_URL}@" .env
|
||||
fi
|
||||
|
||||
# ============================================
|
||||
# Run Database Migrations
|
||||
# ============================================
|
||||
echo "[4/6] Running database migrations..."
|
||||
php artisan migrate --force
|
||||
|
||||
# ============================================
|
||||
# Laravel Optimizations
|
||||
# ============================================
|
||||
echo "[5/6] Optimizing Laravel..."
|
||||
php artisan config:cache
|
||||
php artisan route:cache
|
||||
php artisan view:cache
|
||||
|
||||
# ============================================
|
||||
# Stop temporary MySQL instance
|
||||
# ============================================
|
||||
echo "[6/6] Stopping temporary MySQL instance..."
|
||||
mysqladmin -u root -p"${MYSQL_ROOT_PASSWORD}" shutdown
|
||||
wait $MYSQL_PID
|
||||
|
||||
echo "=========================================="
|
||||
echo "Initialization complete!"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Database Credentials (save these!):"
|
||||
echo " Database: ${DB_DATABASE}"
|
||||
echo " Username: ${DB_USERNAME}"
|
||||
echo " Password: ${DB_PASSWORD}"
|
||||
echo " Root Password: ${MYSQL_ROOT_PASSWORD}"
|
||||
echo ""
|
||||
echo "Starting all services with supervisord..."
|
||||
echo "=========================================="
|
||||
|
||||
# Execute the command passed to the container (supervisord)
|
||||
exec "$@"
|
||||
109
.docker/production/nginx.conf
Normal file
109
.docker/production/nginx.conf
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
server_name _;
|
||||
|
||||
# Logging
|
||||
access_log /var/log/nginx/access.log;
|
||||
error_log /var/log/nginx/error.log;
|
||||
|
||||
# =========================================
|
||||
# Frontend Static Assets (served directly by nginx) - MUST BE FIRST
|
||||
# =========================================
|
||||
# Use ^~ to prevent regex locations from matching
|
||||
location ^~ /assets/ {
|
||||
alias /var/www/frontend/build/client/assets/;
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# =========================================
|
||||
# Backend API Routes (Laravel)
|
||||
# =========================================
|
||||
root /var/www/backend/public;
|
||||
index index.php index.html;
|
||||
|
||||
location ~ ^/(api|sanctum)/ {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_pass 127.0.0.1:9000;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
include fastcgi_params;
|
||||
|
||||
# Laravel-specific settings
|
||||
fastcgi_param HTTP_PROXY "";
|
||||
fastcgi_read_timeout 300;
|
||||
fastcgi_buffer_size 128k;
|
||||
fastcgi_buffers 256 16k;
|
||||
fastcgi_busy_buffers_size 256k;
|
||||
fastcgi_temp_file_write_size 256k;
|
||||
}
|
||||
}
|
||||
|
||||
# Handle API routes that don't map to files
|
||||
location ~ ^/(api|sanctum) {
|
||||
try_files $uri /index.php?$query_string;
|
||||
}
|
||||
|
||||
# PHP file handler for backend
|
||||
location ~ \.php$ {
|
||||
fastcgi_pass 127.0.0.1:9000;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
include fastcgi_params;
|
||||
}
|
||||
|
||||
# =========================================
|
||||
# Frontend Routes (React Router via Node.js)
|
||||
# =========================================
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
# Standard proxy headers
|
||||
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;
|
||||
|
||||
# WebSocket support (for React Router HMR if needed)
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# Timeouts
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
|
||||
# =========================================
|
||||
# Security & Performance
|
||||
# =========================================
|
||||
|
||||
# Deny access to hidden files
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
|
||||
# Disable logging for favicon and robots.txt
|
||||
location = /favicon.ico {
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
|
||||
location = /robots.txt {
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
|
||||
# Cache static assets
|
||||
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
}
|
||||
52
.docker/production/supervisord.conf
Normal file
52
.docker/production/supervisord.conf
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
[supervisord]
|
||||
nodaemon=true
|
||||
user=root
|
||||
logfile=/var/log/supervisor/supervisord.log
|
||||
pidfile=/var/run/supervisord.pid
|
||||
loglevel=info
|
||||
|
||||
[program:mysql]
|
||||
command=/usr/sbin/mysqld --user=mysql --console
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stdout_logfile=/var/log/supervisor/mysql.log
|
||||
stderr_logfile=/var/log/supervisor/mysql_error.log
|
||||
priority=1
|
||||
|
||||
[program:php-fpm]
|
||||
command=/usr/sbin/php-fpm8.2 -F
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stdout_logfile=/var/log/supervisor/php-fpm.log
|
||||
stderr_logfile=/var/log/supervisor/php-fpm_error.log
|
||||
priority=10
|
||||
|
||||
[program:nginx]
|
||||
command=/usr/sbin/nginx -g "daemon off;"
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stdout_logfile=/var/log/supervisor/nginx.log
|
||||
stderr_logfile=/var/log/supervisor/nginx_error.log
|
||||
priority=20
|
||||
|
||||
[program:frontend]
|
||||
command=/usr/bin/node /var/www/frontend/node_modules/@react-router/serve/dist/cli.js /var/www/frontend/build/server/index.js
|
||||
directory=/var/www/frontend
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stdout_logfile=/var/log/supervisor/frontend.log
|
||||
stderr_logfile=/var/log/supervisor/frontend_error.log
|
||||
environment=PORT=3000,NODE_ENV=production
|
||||
priority=30
|
||||
user=www-data
|
||||
|
||||
[program:queue-worker]
|
||||
command=/usr/bin/php /var/www/backend/artisan queue:work --sleep=3 --tries=3 --max-time=3600
|
||||
directory=/var/www/backend
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stdout_logfile=/var/log/supervisor/queue-worker.log
|
||||
stderr_logfile=/var/log/supervisor/queue-worker_error.log
|
||||
priority=40
|
||||
user=www-data
|
||||
numprocs=1
|
||||
|
|
@ -49,6 +49,10 @@ fi
|
|||
echo -e "${GREEN}=== Backend Setup ===${NC}"
|
||||
cd "$PROJECT_ROOT/backend"
|
||||
|
||||
# Set compose file paths
|
||||
COMPOSE_FILE="$PROJECT_ROOT/.docker/development/docker-compose.yml"
|
||||
COMPOSE_OVERRIDE="$PROJECT_ROOT/.docker/development/docker-compose.override.yml"
|
||||
|
||||
# Install dependencies if vendor doesn't exist
|
||||
if [ ! -d "vendor" ]; then
|
||||
echo -e "${YELLOW}No vendor directory found. Installing dependencies with Docker...${NC}"
|
||||
|
|
@ -88,7 +92,7 @@ fi
|
|||
|
||||
# Start containers using compose directly (docker-compose.override.yml is automatically used)
|
||||
echo -e "${YELLOW}Starting backend containers...${NC}"
|
||||
if $CONTAINER_CLI compose up -d 2>&1 | tee /tmp/compose-up.log; then
|
||||
if $CONTAINER_CLI compose -f "$COMPOSE_FILE" -f "$COMPOSE_OVERRIDE" up -d 2>&1 | tee /tmp/compose-up.log; then
|
||||
echo -e "${GREEN}✓ Containers started${NC}\n"
|
||||
else
|
||||
echo -e "${RED}✗ Failed to start containers. Check /tmp/compose-up.log for details${NC}"
|
||||
|
|
@ -100,7 +104,7 @@ echo -e "${YELLOW}Waiting for database to be ready...${NC}"
|
|||
MAX_ATTEMPTS=30
|
||||
ATTEMPT=0
|
||||
while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
|
||||
if $CONTAINER_CLI compose exec mysql mysqladmin ping -h localhost --silent 2>/dev/null; then
|
||||
if $CONTAINER_CLI compose -f "$COMPOSE_FILE" -f "$COMPOSE_OVERRIDE" exec mysql mysqladmin ping -h localhost --silent 2>/dev/null; then
|
||||
echo -e "${GREEN}✓ Database is ready${NC}"
|
||||
break
|
||||
fi
|
||||
|
|
@ -124,11 +128,11 @@ fi
|
|||
|
||||
# Run migrations
|
||||
echo -e "${YELLOW}Running database migrations...${NC}"
|
||||
$CONTAINER_CLI compose exec backend php artisan migrate --force
|
||||
$CONTAINER_CLI compose -f "$COMPOSE_FILE" -f "$COMPOSE_OVERRIDE" exec backend php artisan migrate --force
|
||||
|
||||
# Check if database has data
|
||||
echo -e "${YELLOW}Checking if database needs seeding...${NC}"
|
||||
TABLE_COUNT=$($CONTAINER_CLI compose exec backend php artisan tinker --execute="echo \DB::table('users')->count();" 2>/dev/null | tail -1 | tr -d '[:space:]' || echo "0")
|
||||
TABLE_COUNT=$($CONTAINER_CLI compose -f "$COMPOSE_FILE" -f "$COMPOSE_OVERRIDE" exec backend php artisan tinker --execute="echo \DB::table('users')->count();" 2>/dev/null | tail -1 | tr -d '[:space:]' || echo "0")
|
||||
# Default to 0 if not a number
|
||||
if ! [[ "$TABLE_COUNT" =~ ^[0-9]+$ ]]; then
|
||||
TABLE_COUNT=0
|
||||
|
|
@ -137,7 +141,7 @@ if [ "$TABLE_COUNT" -eq "0" ]; then
|
|||
echo -e "${YELLOW}Database is empty. Run seeders? (y/n)${NC}"
|
||||
read -r response
|
||||
if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
|
||||
$CONTAINER_CLI compose exec backend php artisan db:seed
|
||||
$CONTAINER_CLI compose -f "$COMPOSE_FILE" -f "$COMPOSE_OVERRIDE" exec backend php artisan db:seed
|
||||
echo -e "${GREEN}✓ Database seeded${NC}\n"
|
||||
fi
|
||||
fi
|
||||
|
|
@ -153,7 +157,7 @@ echo -e "${GREEN}Database:${NC} MySQL on localhost:3306"
|
|||
echo -e ""
|
||||
echo -e "${YELLOW}Note:${NC} Frontend container will install dependencies and start automatically."
|
||||
echo -e "${YELLOW}To view frontend logs:${NC}"
|
||||
echo -e " cd backend && $CONTAINER_CLI compose logs -f frontend"
|
||||
echo -e " $CONTAINER_CLI compose -f .docker/development/docker-compose.yml -f .docker/development/docker-compose.override.yml logs -f frontend"
|
||||
echo -e ""
|
||||
echo -e "${YELLOW}To stop all services:${NC}"
|
||||
echo -e " cd backend && $CONTAINER_CLI compose down"
|
||||
echo -e " $CONTAINER_CLI compose -f .docker/development/docker-compose.yml -f .docker/development/docker-compose.override.yml down"
|
||||
|
|
|
|||
Loading…
Reference in a new issue