Skip to main content

Service Overview

Macuin runs four Docker services that work together to provide a complete development environment. Each service has a specific role in the application stack.

Nginx

Image: nginx:1.27-alpinePort: 8000Web server and reverse proxy

Laravel App

Image: Custom (PHP 8.3-FPM)Port: 9000 (internal)PHP application container

PostgreSQL

Image: postgres:16-alpinePort: 5432Database service

Node

Image: node:22-alpinePort: 5173Frontend build tools

Nginx Service

Overview

Nginx serves as the web server and entry point for HTTP requests. It handles static files and proxies PHP requests to the Laravel application container.

Configuration Details

nginx:
  image: nginx:1.27-alpine
  container_name: laravel_nginx
  ports:
    - "8000:80"
  volumes:
    - ./:/var/www/html:ro
    - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
  depends_on:
    - app
  networks:
    - laravel_network

Key Features

  • Alpine Linux: Minimal image size (~40MB)
  • Laravel Routing: Configured for Laravel’s front controller pattern
  • Security: Blocks access to hidden files
  • FastCGI: Optimized communication with PHP-FPM

Common Commands

docker-compose up -d nginx

Troubleshooting

The Nginx container can’t reach PHP-FPM:
# Check if app container is running
docker-compose ps app

# Check app container logs
docker-compose logs app

# Verify network connectivity
docker-compose exec nginx ping app
Laravel routing may not be properly configured:
# Verify Nginx config syntax
docker-compose exec nginx nginx -t

# Check document root
docker-compose exec nginx ls /var/www/html/public

Laravel App Service

Overview

The core PHP application runs in a custom PHP-FPM container. This service executes all Laravel code and handles business logic.

Configuration Details

app:
  build:
    context: .
    dockerfile: docker/php/Dockerfile
    args:
      UID: "1000"
      GID: "1000"
  container_name: laravel_app
  working_dir: /var/www/html
  volumes:
    - ./:/var/www/html
  environment:
    DB_CONNECTION: pgsql
    DB_HOST: postgres
    DB_PORT: 5432
    DB_DATABASE: laravel
    DB_USERNAME: laravel
    DB_PASSWORD: secret
  depends_on:
    postgres:
      condition: service_healthy
  networks:
    - laravel_network

Installed PHP Extensions

  • pdo: PHP Data Objects
  • pdo_pgsql: PostgreSQL driver for PDO

Environment Variables

VariableValuePurpose
DB_CONNECTIONpgsqlDatabase driver
DB_HOSTpostgresDatabase hostname
DB_PORT5432Database port
DB_DATABASElaravelDatabase name
DB_USERNAMElaravelDatabase user
DB_PASSWORDsecretDatabase password

Common Commands

docker-compose exec app bash

Development Workflow

1

Code Changes

Edit files on your host machine. Changes are immediately reflected in the container via volume mount.
2

Database Migrations

docker-compose exec app php artisan migrate
3

Install Dependencies

docker-compose exec app composer require vendor/package
4

Clear Cache

docker-compose exec app php artisan optimize:clear

Troubleshooting

File permission issues between host and container:
# Check current UID/GID
id -u
id -g

# Rebuild with correct UID/GID
docker-compose build --build-arg UID=$(id -u) --build-arg GID=$(id -g) app
docker-compose up -d app
Laravel can’t connect to PostgreSQL:
# Check PostgreSQL is healthy
docker-compose ps postgres

# Test connection from app container
docker-compose exec app php artisan db:show

# Check environment variables
docker-compose exec app env | grep DB_
Composer runs out of memory:
docker-compose exec app php -d memory_limit=-1 /usr/bin/composer install

PostgreSQL Service

Overview

PostgreSQL 16 provides the relational database for the application. Data is persisted in a named Docker volume.

Configuration Details

postgres:
  image: postgres:16-alpine
  container_name: laravel_postgres
  environment:
    POSTGRES_DB: laravel
    POSTGRES_USER: laravel
    POSTGRES_PASSWORD: secret
  ports:
    - "5432:5432"
  volumes:
    - postgres_data:/var/lib/postgresql/data
  networks:
    - laravel_network
  healthcheck:
    test: ["CMD-SHELL", "pg_isready -U laravel -d laravel"]
    interval: 5s
    timeout: 3s
    retries: 20

Environment Variables

VariableDefaultDescription
POSTGRES_DBlaravelInitial database name
POSTGRES_USERlaravelSuperuser username
POSTGRES_PASSWORDsecretSuperuser password
Change POSTGRES_PASSWORD before deploying to production!

Common Commands

docker-compose exec postgres psql -U laravel -d laravel

Database Management

docker-compose exec postgres psql -U laravel -d laravel
Common psql commands:
  • \l - List databases
  • \dt - List tables
  • \d table_name - Describe table
  • \q - Exit
docker-compose exec postgres createdb -U laravel new_database
docker-compose exec postgres dropdb -U laravel database_name
This permanently deletes all data in the database!
SELECT * FROM pg_stat_activity WHERE datname = 'laravel';

Data Persistence

The postgres_data named volume stores all database files:
volumes:
  postgres_data:
Data persists across:
  • Container restarts
  • Container recreation
  • docker-compose down
Data is lost when:
  • Volume is explicitly deleted
  • docker-compose down -v is run

Troubleshooting

PostgreSQL may still be starting:
# Check health status
docker-compose ps postgres

# Watch startup logs
docker-compose logs -f postgres

# Verify port is accessible
nc -zv localhost 5432
Credentials may be incorrect:
# Check environment variables
docker-compose exec postgres env | grep POSTGRES

# Try connecting with psql
docker-compose exec postgres psql -U laravel -d laravel
Check volume size:
docker system df -v
docker volume inspect macuin_postgres_data

Node Service

Overview

The Node.js service runs Vite for frontend asset compilation and hot module replacement during development.

Configuration Details

node:
  image: node:22-alpine
  container_name: laravel_node
  working_dir: /var/www/html
  user: "0:0"
  volumes:
    - ./:/var/www/html
    - node_modules:/var/www/html/node_modules
  ports:
    - "5173:5173"
  command: sh -lc "chown -R 1000:1000 /var/www/html/node_modules && npm ci || npm i && npm run dev -- --host 0.0.0.0 --port 5173 --strictPort"

Volume Strategy

volumes:
  - ./:/var/www/html
  - node_modules:/var/www/html/node_modules
The node_modules volume overlay provides:
  • Performance: Faster I/O on Docker Desktop
  • Isolation: Prevents host/container conflicts
  • Consistency: Same packages across team

Common Commands

docker-compose logs -f node

Vite Development Server

Access the Vite dev server at http://localhost:5173 Features:
  • Hot Module Replacement (HMR)
  • Fast rebuild times
  • Error overlay
  • Source maps
Configuration:
vite.config.js
export default {
  server: {
    host: '0.0.0.0',
    port: 5173,
    strictPort: true
  }
}

Troubleshooting

Port 5173 is occupied:
# Find process using port 5173
lsof -i :5173

# Or change port in docker-compose.yml
ports:
  - "5174:5173"
node_modules may need refresh:
# Restart node service
docker-compose restart node

# Or clear and reinstall
docker volume rm macuin_node_modules
docker-compose up -d node
Hot reload may not be connecting:
  1. Check Vite is bound to 0.0.0.0
  2. Verify port 5173 is accessible
  3. Check browser console for WebSocket errors
# Verify Vite config
docker-compose exec node cat vite.config.js
File ownership issues:
# Fix permissions
docker-compose exec node chown -R 1000:1000 /var/www/html/node_modules

Service Management

Starting Services

docker-compose up -d

Stopping Services

docker-compose stop
docker-compose down -v deletes all data in volumes, including your database!

Viewing Logs

docker-compose logs -f

Service Status

# View running containers
docker-compose ps

# View detailed status
docker-compose ps -a

# View resource usage
docker stats

Executing Commands

docker-compose exec app bash

Health Monitoring

Check Service Health

# View health status
docker-compose ps

# Inspect specific service
docker inspect laravel_postgres | grep -A 10 Health

# Watch health checks
watch -n 1 'docker-compose ps'

Service Dependencies

The startup order ensures dependencies are ready:
postgres (healthy) → app → nginx
                            node (independent)

Best Practices

Development

  • Keep containers running during development
  • Use exec for running commands
  • Monitor logs for errors
  • Restart services after config changes

Performance

  • Use named volumes for better I/O
  • Limit log retention
  • Prune unused resources regularly
  • Use Alpine images where possible

Security

  • Change default passwords
  • Use environment files for secrets
  • Run as non-root users
  • Keep images updated

Maintenance

  • Backup database regularly
  • Monitor disk space
  • Update images periodically
  • Review logs for warnings

Quick Reference

Port Mappings

ServiceContainer PortHost PortURL
Nginx808000http://localhost:8000
PostgreSQL54325432localhost:5432
Node (Vite)51735173http://localhost:5173
PHP-FPM9000-Internal only

Volume Names

  • macuin_postgres_data - Database files
  • macuin_node_modules - Node.js packages

Network Name

  • macuin_laravel_network - Bridge network for inter-service communication

Next Steps

Configuration

Customize Docker Compose settings

Overview

Review the architecture overview

Build docs developers (and LLMs) love