Deploy Health Manager in a containerized environment using Docker. This method provides consistency across different platforms and simplifies deployment.
Prerequisites
Install Docker and Docker Compose on your system:
Ubuntu/Debian
CentOS/RHEL
macOS
Windows
# Install Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# Install Docker Compose
sudo apt-get update
sudo apt-get install docker-compose-plugin
# Add your user to docker group
sudo usermod -aG docker $USER
Verify installation:
docker --version
docker compose version
Understanding the Dockerfile
Health Manager uses a multi-stage build for optimal production deployment:
Stage 1: Frontend Build
FROM node:22-alpine AS frontend
WORKDIR /app
# Install dependencies
COPY package.json package-lock.json* ./
RUN npm ci
# Build production assets
COPY . .
RUN npm run build
This stage compiles all frontend assets (CSS, JavaScript) using Vite.
Stage 2: Production Runtime
FROM php:8.4-fpm
WORKDIR /var/www/html
# Install system dependencies
RUN apt-get update && apt-get install -y \
nginx \
supervisor \
libpq-dev \
libzip-dev \
# ... more dependencies
The production image includes:
PHP 8.4 FPM for processing PHP code
Nginx as the web server
Supervisor for process management
PostgreSQL support (default database)
OPcache for PHP optimization
Quick Start with Docker
Clone and Build
git clone https://github.com/formatocd/health-manager.git
cd health-manager
Build the Docker Image
docker build -t health-manager:latest .
This builds the image with all dependencies included. Build time: ~5-10 minutes.
Run the Container
For a quick test with SQLite:
docker run -d \
--name health-manager \
-p 8080:80 \
-e DB_CONNECTION=sqlite \
health-manager:latest
Access the application at http://localhost:8080
This basic setup uses ephemeral storage. Data will be lost when the container is removed. Use Docker Compose for production.
Production Deployment with Docker Compose
For production deployments, use Docker Compose to orchestrate multiple containers.
Create Docker Compose Configuration
Create docker-compose.yml in your project root:
version : '3.8'
services :
app :
build :
context : .
dockerfile : Dockerfile
container_name : health-manager-app
restart : unless-stopped
ports :
- "8080:80"
environment :
# Application
APP_NAME : "Health Manager"
APP_ENV : production
APP_DEBUG : "false"
APP_URL : "http://localhost:8080"
APP_LOCALE : en
# Database - PostgreSQL
DB_CONNECTION : pgsql
DB_HOST : db
DB_PORT : 5432
DB_DATABASE : health_manager
DB_USERNAME : healthuser
DB_PASSWORD : secure_password_change_this
# Mail Configuration
MAIL_MAILER : smtp
MAIL_HOST : smtp.gmail.com
MAIL_PORT : 587
MAIL_USERNAME : [email protected]
MAIL_PASSWORD : your-app-password
MAIL_ENCRYPTION : tls
MAIL_FROM_ADDRESS : "[email protected] "
MAIL_FROM_NAME : "Health Manager"
volumes :
- app-storage:/var/www/html/storage/app
- app-logs:/var/www/html/storage/logs
depends_on :
- db
networks :
- health-manager-network
db :
image : postgres:16-alpine
container_name : health-manager-db
restart : unless-stopped
environment :
POSTGRES_DB : health_manager
POSTGRES_USER : healthuser
POSTGRES_PASSWORD : secure_password_change_this
volumes :
- db-data:/var/lib/postgresql/data
networks :
- health-manager-network
healthcheck :
test : [ "CMD-SHELL" , "pg_isready -U healthuser" ]
interval : 10s
timeout : 5s
retries : 5
volumes :
db-data :
driver : local
app-storage :
driver : local
app-logs :
driver : local
networks :
health-manager-network :
driver : bridge
Security: Change the default passwords before deploying to production!
Alternative: MySQL Configuration
To use MySQL instead of PostgreSQL:
MySQL Service
App Environment (MySQL)
db :
image : mysql:8.0
container_name : health-manager-db
restart : unless-stopped
environment :
MYSQL_DATABASE : health_manager
MYSQL_USER : healthuser
MYSQL_PASSWORD : secure_password_change_this
MYSQL_ROOT_PASSWORD : root_password_change_this
volumes :
- db-data:/var/lib/mysql
networks :
- health-manager-network
command : --default-authentication-plugin=mysql_native_password
healthcheck :
test : [ "CMD" , "mysqladmin" , "ping" , "-h" , "localhost" ]
interval : 10s
timeout : 5s
retries : 5
Deploy with Docker Compose
Builds the application image (if not already built)
Starts the database container
Starts the application container
Creates necessary volumes and networks
Verify all containers are running:
NAME STATUS PORTS
health-manager-app Up 2 minutes 0.0.0.0:8080->80/tcp
health-manager-db Up 2 minutes 5432/tcp
Monitor application logs:
# All services
docker compose logs -f
# Application only
docker compose logs -f app
# Database only
docker compose logs -f db
Open your browser and navigate to:
Environment Configuration
Using .env File
Instead of defining environment variables in docker-compose.yml, use a .env file:
# Application
APP_NAME = "Health Manager"
APP_ENV = production
APP_DEBUG = false
APP_URL = https://yourdomain.com
APP_KEY = # Will be auto-generated on first run
# Database
DB_CONNECTION = pgsql
DB_HOST = db
DB_PORT = 5432
DB_DATABASE = health_manager
DB_USERNAME = healthuser
DB_PASSWORD = your_secure_password_here
# Mail
MAIL_MAILER = smtp
MAIL_HOST = smtp.yourserver.com
MAIL_PORT = 587
MAIL_USERNAME = [email protected]
MAIL_PASSWORD = your_email_password
MAIL_ENCRYPTION = tls
MAIL_FROM_ADDRESS = "[email protected] "
MAIL_FROM_NAME = "${ APP_NAME }"
Update docker-compose.yml to use the env file:
services :
app :
env_file :
- .env
# ... rest of configuration
Container Management
Start Containers
Stop Containers
Restart Containers
Stop and Remove Containers
Using docker compose down -v will also delete volumes, removing all data . Use with caution!
Rebuild After Changes
After modifying the Dockerfile or code:
docker compose up -d --build
Running Commands Inside Containers
Execute Artisan Commands
# Clear cache
docker compose exec app php artisan cache:clear
# Run migrations manually (not usually needed)
docker compose exec app php artisan migrate
# Create a user
docker compose exec app php artisan tinker
Access Container Shell
docker compose exec app bash
Database Access
# Access PostgreSQL CLI
docker compose exec db psql -U healthuser -d health_manager
# Backup database
docker compose exec db pg_dump -U healthuser health_manager > backup.sql
# Restore database
docker compose exec -T db psql -U healthuser health_manager < backup.sql
SSL/HTTPS Configuration
Option 1: Reverse Proxy (Recommended)
Use Nginx or Traefik as a reverse proxy with automatic SSL:
Nginx Proxy with Let's Encrypt
Traefik with Let's Encrypt
services :
nginx-proxy :
image : nginxproxy/nginx-proxy
container_name : nginx-proxy
ports :
- "80:80"
- "443:443"
volumes :
- /var/run/docker.sock:/tmp/docker.sock:ro
- nginx-certs:/etc/nginx/certs
- nginx-vhost:/etc/nginx/vhost.d
- nginx-html:/usr/share/nginx/html
networks :
- health-manager-network
letsencrypt :
image : nginxproxy/acme-companion
container_name : nginx-proxy-acme
volumes :
- /var/run/docker.sock:/var/run/docker.sock:ro
- nginx-certs:/etc/nginx/certs
- nginx-vhost:/etc/nginx/vhost.d
- nginx-html:/usr/share/nginx/html
environment :
- [email protected]
depends_on :
- nginx-proxy
networks :
- health-manager-network
app :
environment :
- VIRTUAL_HOST=yourdomain.com
- LETSENCRYPT_HOST=yourdomain.com
- [email protected]
# Remove ports since nginx-proxy handles them
Option 2: Mount SSL Certificates
If you have existing SSL certificates:
services :
app :
volumes :
- ./certs:/etc/nginx/certs:ro
- ./nginx-ssl.conf:/etc/nginx/conf.d/default.conf:ro
Data Persistence & Backups
Volume Locations
Docker volumes store persistent data:
# List volumes
docker volume ls
# Inspect volume
docker volume inspect health-manager_db-data
# Locate volume on filesystem
docker volume inspect health-manager_db-data | grep Mountpoint
Backup Strategy
Create backup.sh: #!/bin/bash
BACKUP_DIR = "./backups/$( date +%Y%m%d_%H%M%S)"
mkdir -p " $BACKUP_DIR "
echo "Creating backup..."
# Backup database
docker compose exec -T db pg_dump -U healthuser health_manager > " $BACKUP_DIR /database.sql"
# Backup uploaded files
docker compose exec -T app tar czf - -C /var/www/html/storage/app . > " $BACKUP_DIR /storage.tar.gz"
# Backup .env file
cp .env " $BACKUP_DIR /.env"
echo "Backup completed: $BACKUP_DIR "
# Keep only last 7 days of backups
find ./backups -type d -mtime +7 -exec rm -rf {} +
Make executable and run: chmod +x backup.sh
./backup.sh
Schedule with cron: # Daily backup at 2 AM
0 2 * * * cd /path/to/health-manager && ./backup.sh
Restore from Backup
# Stop containers
docker compose down
# Restore database
docker compose up -d db
docker compose exec -T db psql -U healthuser health_manager < backups/20240101/database.sql
# Restore storage
docker compose exec -T app tar xzf - -C /var/www/html/storage/app < backups/20240101/storage.tar.gz
# Start all services
docker compose up -d
Adjust PHP-FPM Workers
Create custom PHP-FPM configuration:
[www]
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 500
Mount in docker-compose.yml:
services :
app :
volumes :
- ./docker/php-fpm.conf:/usr/local/etc/php-fpm.d/www.conf:ro
Resource Limits
Limit container resource usage:
services :
app :
deploy :
resources :
limits :
cpus : '2'
memory : 2G
reservations :
cpus : '1'
memory : 1G
Monitoring & Logs
View Real-time Logs
# All services
docker compose logs -f
# Specific service with timestamps
docker compose logs -f --timestamps app
# Last 100 lines
docker compose logs --tail=100 app
Container Stats
Shows CPU, memory, network, and disk usage for all running containers.
Health Checks
Add health check to application:
services :
app :
healthcheck :
test : [ "CMD" , "curl" , "-f" , "http://localhost/" ]
interval : 30s
timeout : 10s
retries : 3
start_period : 40s
Troubleshooting
Check logs:
Verify environment variables:
Check port conflicts:
Restart services:
Database Connection Failed
Verify database is running:
Check database logs:
Test database connection:
docker compose exec app php artisan tinker
>>> DB::connection () ->getPdo ();
Verify credentials match between .env and database service
Fix storage permissions:
docker compose exec app chown -R www-data:www-data /var/www/html/storage
docker compose exec app chmod -R 775 /var/www/html/storage
Rebuild container:
docker compose down
docker compose up -d --build
Check container memory usage:
Increase memory limit in docker-compose.yml
Reduce PHP-FPM workers in configuration
Check for memory leaks in application logs
Updates & Maintenance
Update Application
# Pull latest code
git pull origin main
# Rebuild and restart
docker compose up -d --build
# Clear cache
docker compose exec app php artisan config:cache
docker compose exec app php artisan route:cache
docker compose exec app php artisan view:cache
Update Docker Images
# Pull latest base images
docker compose pull
# Rebuild application
docker compose up -d --build
Clean Up
Remove unused Docker resources:
# Remove unused images
docker image prune -a
# Remove unused volumes (BE CAREFUL!)
docker volume prune
# Remove everything unused
docker system prune -a --volumes
Danger: docker system prune with --volumes will delete all unused volumes, potentially losing data!
Production Best Practices
Use specific image tags instead of latest
Set resource limits for containers
Implement health checks for all services
Use secrets management for sensitive data (Docker Secrets, Vault)
Enable log rotation to prevent disk space issues
Monitor containers with tools like Prometheus, Grafana
Regular backups of database and uploaded files
Use Docker Swarm or Kubernetes for high availability
Implement CI/CD for automated deployments
Security scanning of images with tools like Trivy
Next Steps
Set up reverse proxy with SSL
Configure automated backups
Implement monitoring and alerting
Set up log aggregation (ELK stack, Loki)
Configure auto-scaling (Kubernetes)
Implement CI/CD pipeline