Refactor nginx setup to use host-level configuration

- Remove main nginx ingress container from docker-compose
- Add minimal api-nginx container for FastCGI to HTTP conversion
- Expose services directly on ports 7654 (API) and 7655 (UI)
- Add comprehensive NGINX_SETUP.md documentation
- Include example host nginx configuration
- Update docker setup script for new architecture

This change allows OpnForm to integrate better with existing host nginx
setups by removing the containerized ingress and exposing services
directly to the host for reverse proxy configuration.
This commit is contained in:
Matt 2025-06-05 17:42:01 +02:00
parent a11fb01bef
commit 3a8e601a37
5 changed files with 314 additions and 34 deletions

131
NGINX_SETUP.md Normal file
View File

@ -0,0 +1,131 @@
# OpnForm Nginx Setup Guide
This guide explains how to set up OpnForm with a host-level nginx configuration.
## Architecture Overview
The modified setup removes the main nginx ingress container and exposes services directly:
- **UI Service**: Exposed on port 7655 (HTTP)
- **API Service**: Exposed on port 7654 (HTTP via minimal nginx container)
- **Database**: PostgreSQL (internal only)
- **Redis**: Cache service (internal only)
## Key Changes from Default Setup
1. **Removed YAML anchors** - Each container now has its own explicit configuration to avoid conflicts
2. **Removed main ingress container** - Your host nginx handles all routing
3. **Added minimal api-nginx** - Small nginx container just to convert FastCGI to HTTP for the API
4. **Custom ports** - Using 7654-7655 range to avoid conflicts
## Setup Steps
### 1. Stop any existing containers
```bash
docker compose down
docker compose -f docker-compose.dev.yml down
```
### 2. Run the setup script
```bash
./scripts/docker-setup.sh
```
### 3. Verify services are running
```bash
docker compose ps
```
You should see:
- opnform-api (healthy)
- opnform-api-nginx (healthy)
- opnform-api-worker (running)
- opnform-api-scheduler (running)
- opnform-client (healthy)
- opnform-redis (healthy)
- opnform-db (healthy)
### 4. Configure your host nginx
Copy the example configuration:
```bash
sudo cp nginx-host-example.conf /etc/nginx/sites-available/forms.portnimara.dev
sudo ln -s /etc/nginx/sites-available/forms.portnimara.dev /etc/nginx/sites-enabled/
```
Edit the file to adjust:
- SSL certificate paths
- Server name if different
- Any other site-specific settings
### 5. Test nginx configuration
```bash
sudo nginx -t
```
### 6. Reload nginx
```bash
sudo systemctl reload nginx
```
## Troubleshooting
### Port already in use
If you get "port already allocated" errors:
1. Check what's using the ports:
```bash
sudo lsof -i :7654
sudo lsof -i :7655
```
2. Stop conflicting services or change the ports in docker-compose.yml
### API not responding
1. Check the api-nginx logs:
```bash
docker logs opnform-api-nginx
```
2. Verify the API container is running:
```bash
docker logs opnform-api
```
### UI not loading
1. Check the client logs:
```bash
docker logs opnform-client
```
2. Ensure the client/.env file has correct API URL settings
## Port Reference
- **7654**: API (HTTP) - proxied through api-nginx to PHP-FPM
- **7655**: UI (HTTP) - Nuxt.js frontend
- **9000**: PHP-FPM (internal only, FastCGI protocol)
- **5432**: PostgreSQL (internal only)
- **6379**: Redis (internal only)
## Security Notes
1. Ports are bound to 127.0.0.1 only, not exposed to external network
2. All traffic should go through your host nginx with SSL
3. The minimal api-nginx container only handles FastCGI conversion, no SSL termination
## Default Credentials
- Email: admin@opnform.com
- Password: password
**Important**: Change these immediately after first login!

View File

@ -1,11 +1,10 @@
---
services:
api: &api-environment
api:
image: jhumanj/opnform-api:latest
container_name: opnform-api
volumes: &api-environment-volumes
volumes:
- opnform_storage:/usr/share/nginx/html/storage:rw
environment: &api-env
environment:
APP_ENV: production
# Database settings
DB_HOST: db
@ -25,7 +24,7 @@ services:
db:
condition: service_healthy
redis:
condition: service_healthy # Depend on redis being healthy too
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "php /usr/share/nginx/html/artisan about || exit 1"]
interval: 30s
@ -33,13 +32,49 @@ services:
retries: 3
start_period: 60s
api-nginx:
image: nginx:alpine
container_name: opnform-api-nginx
volumes:
- ./docker/api-nginx.conf:/etc/nginx/nginx.conf:ro
- opnform_storage:/usr/share/nginx/html/storage:ro
ports:
- "127.0.0.1:7654:80" # API on port 7654
depends_on:
- api
healthcheck:
test: ["CMD-SHELL", "wget --spider -q http://localhost/ || exit 1"]
interval: 30s
timeout: 10s
retries: 3
api-worker:
<<: *api-environment
image: jhumanj/opnform-api:latest
container_name: opnform-api-worker
command: ["php", "artisan", "queue:work"]
volumes:
- opnform_storage:/usr/share/nginx/html/storage:rw
environment:
<<: *api-env
APP_ENV: production
# Database settings
DB_HOST: db
REDIS_HOST: redis
DB_DATABASE: ${DB_DATABASE:-forge}
DB_USERNAME: ${DB_USERNAME:-forge}
DB_PASSWORD: ${DB_PASSWORD:-forge}
DB_CONNECTION: ${DB_CONNECTION:-pgsql}
# PHP Configuration
PHP_MEMORY_LIMIT: "1G"
PHP_MAX_EXECUTION_TIME: "600"
PHP_UPLOAD_MAX_FILESIZE: "64M"
PHP_POST_MAX_SIZE: "64M"
env_file:
- ./api/.env
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "pgrep -f 'php artisan queue:work' > /dev/null || exit 1"]
interval: 60s
@ -48,22 +83,44 @@ services:
start_period: 30s
api-scheduler:
<<: *api-environment
image: jhumanj/opnform-api:latest
container_name: opnform-api-scheduler
command: ["php", "artisan", "schedule:work"]
volumes:
- opnform_storage:/usr/share/nginx/html/storage:rw
environment:
<<: *api-env
APP_ENV: production
# Database settings
DB_HOST: db
REDIS_HOST: redis
DB_DATABASE: ${DB_DATABASE:-forge}
DB_USERNAME: ${DB_USERNAME:-forge}
DB_PASSWORD: ${DB_PASSWORD:-forge}
DB_CONNECTION: ${DB_CONNECTION:-pgsql}
# PHP Configuration
PHP_MEMORY_LIMIT: "1G"
PHP_MAX_EXECUTION_TIME: "600"
PHP_UPLOAD_MAX_FILESIZE: "64M"
PHP_POST_MAX_SIZE: "64M"
env_file:
- ./api/.env
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "php /usr/share/nginx/html/artisan app:scheduler-status --mode=check --max-minutes=3 || exit 1"]
interval: 60s
timeout: 30s
retries: 3
start_period: 70s # Allow time for first scheduled run and cache write
start_period: 70s
ui:
image: jhumanj/opnform-client:latest
container_name: opnform-client
ports:
- "127.0.0.1:7655:3000" # UI on port 7655
env_file:
- ./client/.env
healthcheck:
@ -95,27 +152,6 @@ services:
volumes:
- postgres-data:/var/lib/postgresql/data
ingress:
image: nginx:1
container_name: opnform-ingress
volumes:
- ./docker/nginx.conf:/etc/nginx/templates/default.conf.template
ports:
- 80:80
environment:
- NGINX_MAX_BODY_SIZE=64m
depends_on:
api:
condition: service_started
ui:
condition: service_started
healthcheck:
test: ["CMD-SHELL", "nginx -t && curl -f http://localhost/ || exit 1"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
volumes:
postgres-data:
opnform_storage:

43
docker/api-nginx.conf Normal file
View File

@ -0,0 +1,43 @@
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
server {
listen 80;
server_name _;
root /usr/share/nginx/html/public;
index index.php;
client_max_body_size 64M;
# Logging
access_log /dev/stdout;
error_log /dev/stderr;
# Handle all requests through PHP
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
# PHP-FPM configuration
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass opnform-api:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html/public/index.php;
fastcgi_param DOCUMENT_ROOT /usr/share/nginx/html/public;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_read_timeout 300;
}
# Deny access to . files
location ~ /\. {
deny all;
}
}
}

67
nginx-host-example.conf Normal file
View File

@ -0,0 +1,67 @@
# Example nginx configuration for forms.portnimara.dev
# Place this in /etc/nginx/sites-available/forms.portnimara.dev
# Then create a symlink: ln -s /etc/nginx/sites-available/forms.portnimara.dev /etc/nginx/sites-enabled/
server {
listen 80;
server_name forms.portnimara.dev;
# Redirect HTTP to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name forms.portnimara.dev;
# SSL certificates - adjust paths as needed
ssl_certificate /etc/letsencrypt/live/forms.portnimara.dev/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/forms.portnimara.dev/privkey.pem;
# SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Client upload size
client_max_body_size 64M;
# Logging
access_log /var/log/nginx/forms.portnimara.dev.access.log;
error_log /var/log/nginx/forms.portnimara.dev.error.log;
# API routes - proxy to the api-nginx container
location ~ ^/(api|open|local/temp|forms/assets)/ {
proxy_pass http://127.0.0.1:7654;
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;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_read_timeout 300s;
proxy_connect_timeout 75s;
}
# Everything else goes to the UI container
location / {
proxy_pass http://127.0.0.1:7655;
proxy_http_version 1.1;
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;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# WebSocket support for hot reload and real-time features
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_cache_bypass $http_upgrade;
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
}

View File

@ -63,7 +63,10 @@ if [ "$DEV_MODE" = true ]; then
else
echo -e "${BLUE}Production environment setup complete!${NC}"
echo -e "${YELLOW}Please wait a moment for all services to start${NC}"
echo -e "${GREEN}Then visit: http://localhost${NC}"
echo -e "${GREEN}Services are available on:${NC}"
echo -e "${GREEN}- UI: http://localhost:7655${NC}"
echo -e "${GREEN}- API: http://localhost:7654${NC}"
echo -e "${YELLOW}Note: Configure your host nginx to proxy to these ports${NC}"
fi
echo -e "${BLUE}Default admin credentials:${NC}"