- Zmiana nazwy: "Norda Biznes Hub" → "Norda Biznes Partner" - Aktualizacja modelu AI: Gemini 2.0 Flash → Gemini 3 Flash - Zachowano historyczne odniesienia w timeline i dokumentacji Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
34 KiB
Critical Configurations Reference
Document Version: 1.0 Last Updated: 2026-01-10 Status: Production LIVE Diagram Type: Configuration Reference / Operations Guide
Overview
This document provides a comprehensive reference of all critical configurations for the Norda Biznes Partner infrastructure. It serves as the single source of truth for:
- NPM reverse proxy configuration (includes critical port 5000 warning)
- Port mappings across all servers and services
- SSL/TLS certificate configuration (Let's Encrypt)
- Environment variables and secrets management
- Database connection strings and credentials
- Gunicorn/WSGI configuration for Flask application
- Systemd service configuration for application lifecycle
- Git repository configuration for deployment
- Critical file paths and directory structure
- Firewall rules and network ACLs
Abstraction Level: Infrastructure Configuration Audience: System Administrators, DevOps Engineers, Production Support Purpose: Configuration reference, incident response, disaster recovery, new server provisioning
⚠️ WARNING: This document contains references to production configurations. Actual secrets are stored in .env files and are never committed to version control.
Related Documentation:
- Deployment Architecture - Infrastructure overview
- Network Topology - Network layout and zones
- HTTP Request Flow - Request path details
- Incident Report 2026-01-02 - NPM port misconfiguration incident
Table of Contents
- NPM Reverse Proxy Configuration
- Port Mappings Reference
- SSL/TLS Configuration
- Environment Variables
- Database Configuration
- Gunicorn WSGI Configuration
- Systemd Service Configuration
- Git Repository Configuration
- Critical File Paths
- Firewall and Network Rules
- Backup and Recovery Locations
- Configuration Management
- Verification Checklist
NPM Reverse Proxy Configuration
⚠️ CRITICAL: Port 5000 Configuration
Proxy Host ID: 27 Domains: nordabiznes.pl, www.nordabiznes.pl
CRITICAL WARNING: NPM must forward to port 5000, NOT port 80!
Forwarding to port 80 causes an infinite redirect loop (ERR_TOO_MANY_REDIRECTS). This was the root cause of the 2026-01-02 production incident.
Complete NPM Configuration
{
"id": 27,
"domain_names": [
"nordabiznes.pl",
"www.nordabiznes.pl"
],
"forward_scheme": "http",
"forward_host": "10.22.68.249",
"forward_port": 5000, // ⚠️ CRITICAL: Must be 5000, NOT 80!
"certificate_id": 27,
"ssl_forced": true,
"http2_support": true,
"block_exploits": true,
"allow_websocket_upgrade": true,
"hsts_enabled": true,
"hsts_subdomains": true,
"advanced_config": "",
"access_list_id": 0,
"meta": {
"letsencrypt_agree": true,
"dns_challenge": false
}
}
NPM Server Details
| Parameter | Value |
|---|---|
| Server | R11-REVPROXY-01 |
| VM ID | 119 |
| IP Address | 10.22.68.250 |
| NPM Version | Latest (Docker) |
| Container Name | nginx-proxy-manager_app_1 |
| Admin Panel | http://10.22.68.250:81 |
| Database | SQLite (/data/database.sqlite) |
Verification Commands
# 1. Check NPM proxy host configuration
ssh maciejpi@10.22.68.250 "docker exec nginx-proxy-manager_app_1 \
sqlite3 /data/database.sqlite \
\"SELECT id, domain_names, forward_host, forward_port FROM proxy_host WHERE id = 27;\""
# Expected output: 27|["nordabiznes.pl","www.nordabiznes.pl"]|10.22.68.249|5000
# 2. Verify website is accessible
curl -I https://nordabiznes.pl/health
# Expected: HTTP/2 200 OK
# 3. Check NPM logs for errors
ssh maciejpi@10.22.68.250 "docker logs nginx-proxy-manager_app_1 --tail 50"
# 4. Verify SSL certificate
openssl s_client -connect nordabiznes.pl:443 -servername nordabiznes.pl < /dev/null 2>/dev/null | \
openssl x509 -noout -dates -subject -issuer
NPM Configuration Update Procedure
IMPORTANT: Always verify port 5000 after making any changes to NPM!
# NPM API configuration update (Python)
import requests
NPM_URL = "http://10.22.68.250:81/api"
NPM_EMAIL = "admin@example.com"
NPM_PASSWORD = "your_npm_password"
# Step 1: Authenticate
auth_response = requests.post(
f"{NPM_URL}/tokens",
json={"identity": NPM_EMAIL, "secret": NPM_PASSWORD}
)
token = auth_response.json()["token"]
headers = {"Authorization": f"Bearer {token}"}
# Step 2: Get current configuration
current = requests.get(
f"{NPM_URL}/nginx/proxy-hosts/27",
headers=headers
).json()
# Step 3: Verify port is 5000
if current["forward_port"] != 5000:
print("⚠️ WARNING: Port is not 5000! Current:", current["forward_port"])
# Update to correct port
current["forward_port"] = 5000
requests.put(
f"{NPM_URL}/nginx/proxy-hosts/27",
headers=headers,
json=current
)
print("✓ Port corrected to 5000")
else:
print("✓ Port is correctly set to 5000")
# Step 4: Verify website works
import subprocess
result = subprocess.run(["curl", "-I", "https://nordabiznes.pl/health"], capture_output=True)
if b"200 OK" in result.stdout:
print("✓ Website is accessible")
else:
print("❌ Website check failed!")
Port Mappings Reference
Complete Port Matrix
| Server | Service | Port | Protocol | Access | Purpose |
|---|---|---|---|---|---|
| Fortigate (WAN) | Public Gateway | 443 | HTTPS | Public | SSL entry point |
| Fortigate (WAN) | HTTP Redirect | 80 | HTTP | Public | Redirect to HTTPS |
| R11-REVPROXY-01 | NPM Proxy | 443 | HTTPS | LAN | SSL termination |
| R11-REVPROXY-01 | NPM Proxy | 80 | HTTP | LAN | HTTP → HTTPS redirect |
| R11-REVPROXY-01 | NPM Admin | 81 | HTTP | LAN | Admin panel |
| R11-REVPROXY-01 | SSH | 22 | SSH | Admin | Remote management |
| NORDABIZ-01 | Flask/Gunicorn | 5000 | HTTP | LAN | Application (CRITICAL!) |
| NORDABIZ-01 | PostgreSQL | 5432 | TCP | Localhost | Database |
| NORDABIZ-01 | Nginx (System) | 80 | HTTP | LAN | ⚠️ DO NOT USE (causes redirect loop) |
| NORDABIZ-01 | Nginx (System) | 443 | HTTPS | LAN | ⚠️ DO NOT USE |
| NORDABIZ-01 | SSH | 22 | SSH | Admin | Remote management |
| r11-git-inpi | Gitea HTTPS | 3000 | HTTPS | LAN | Git repository |
| r11-git-inpi | SSH | 22 | SSH | Admin | Remote management |
NAT/Port Forwarding Rules (Fortigate)
Public → Internal NAT Mappings:
85.237.177.83:443 → 10.22.68.250:443 (NPM Proxy - HTTPS)
85.237.177.83:80 → 10.22.68.250:80 (NPM Proxy - HTTP)
Internal → Internal Routing:
10.22.68.250:* → 10.22.68.249:5000 (NPM → Flask) ⚠️ CRITICAL PORT
10.22.68.249:* → 127.0.0.1:5432 (Flask → PostgreSQL)
10.22.68.249:* → 10.22.68.180:3000 (Flask → Gitea)
Port Usage by Zone
Public Internet Zone:
- Port 443 (HTTPS) - Public website access
- Port 80 (HTTP) - Redirects to HTTPS
DMZ Zone (R11-REVPROXY-01):
- Port 443 (HTTPS) - NPM SSL termination
- Port 80 (HTTP) - NPM HTTP redirect
- Port 81 (HTTP) - NPM admin panel (internal only)
Application Zone (NORDABIZ-01):
- Port 5000 (HTTP) - Flask/Gunicorn application ⚠️ CRITICAL
- Port 5432 (TCP) - PostgreSQL (localhost only)
- Port 80/443 (HTTP/HTTPS) - System nginx (DO NOT USE for app)
Internal Services Zone:
- Port 3000 (HTTPS) - Gitea
SSL/TLS Configuration
Let's Encrypt Certificate
Certificate Details:
- Provider: Let's Encrypt
- Managed By: NPM (Nginx Proxy Manager)
- Certificate ID: 27
- Domains:
- nordabiznes.pl
- www.nordabiznes.pl
- Key Type: RSA 2048-bit
- Validity: 90 days
- Renewal: Automatic (30 days before expiry)
- ACME Challenge: HTTP-01
Certificate Storage:
- Location (NPM):
/data/letsencrypt/live/npm-27/ - Files:
fullchain.pem- Full certificate chainprivkey.pem- Private keycert.pem- Certificate onlychain.pem- Intermediate certificates
TLS Configuration
# NPM TLS Configuration (auto-generated)
ssl_certificate /data/letsencrypt/live/npm-27/fullchain.pem;
ssl_certificate_key /data/letsencrypt/live/npm-27/privkey.pem;
# SSL Protocols
ssl_protocols TLSv1.2 TLSv1.3;
# Cipher Suites (Modern configuration)
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers off;
# SSL Session
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
Security Headers (NPM)
# Headers added by NPM for all requests
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
SSL Verification Commands
# Check certificate expiry
echo | openssl s_client -connect nordabiznes.pl:443 -servername nordabiznes.pl 2>/dev/null | \
openssl x509 -noout -dates
# Test SSL configuration
curl -vI https://nordabiznes.pl 2>&1 | grep -E "(SSL|TLS|expire)"
# Check HSTS header
curl -I https://nordabiznes.pl | grep -i strict
# Test HTTP/2 support
curl -I --http2 https://nordabiznes.pl | head -1
# Comprehensive SSL test (requires ssllabs-scan)
ssllabs-scan --quiet nordabiznes.pl
Certificate Renewal Process
Automatic Renewal (NPM handles this):
- NPM checks certificates 30 days before expiry
- Initiates ACME HTTP-01 challenge with Let's Encrypt
- Creates
.well-known/acme-challenge/endpoint - Let's Encrypt validates domain ownership
- New certificate issued and NPM reloads nginx
- Zero downtime renewal
Manual Renewal (if automatic fails):
# SSH to NPM server
ssh maciejpi@10.22.68.250
# Force certificate renewal
docker exec nginx-proxy-manager_app_1 \
certbot renew --force-renewal
# Reload nginx
docker exec nginx-proxy-manager_app_1 nginx -s reload
# Verify new certificate
curl -vI https://nordabiznes.pl 2>&1 | grep "expire date"
Environment Variables
Production Environment Variables
Location: /var/www/nordabiznes/.env
Owner: www-data:www-data
Permissions: 0600 (read/write owner only)
⚠️ WARNING: Never commit .env to version control!
Required Variables
# Flask Configuration
SECRET_KEY=<random-64-character-hex-string>
FLASK_ENV=production
# Server Configuration
PORT=5000
HOST=0.0.0.0
# Database Configuration
DATABASE_URL=postgresql://nordabiz_app:<password>@127.0.0.1:5432/nordabiz
# Google Gemini API
GOOGLE_GEMINI_API_KEY=<gemini-api-key>
# Google PageSpeed Insights API
GOOGLE_PAGESPEED_API_KEY=<pagespeed-api-key>
# Google Places API
GOOGLE_PLACES_API_KEY=<places-api-key>
# Brave Search API
BRAVE_SEARCH_API_KEY=<brave-api-key>
# Microsoft Graph API (OAuth 2.0)
MS_GRAPH_CLIENT_ID=<client-id>
MS_GRAPH_CLIENT_SECRET=<client-secret>
MS_GRAPH_TENANT_ID=<tenant-id>
# Email Configuration (Microsoft Graph)
MAIL_DEFAULT_SENDER=noreply@nordabiznes.pl
# Application URLs
APP_URL=https://nordabiznes.pl
VERIFY_EMAIL_URL=https://nordabiznes.pl/verify-email
Environment Variable Generation
# Generate SECRET_KEY (64 random hex characters)
python3 -c "import secrets; print(secrets.token_hex(32))"
# Generate random password (32 characters)
openssl rand -base64 32
# Verify .env file permissions
ls -la /var/www/nordabiznes/.env
# Expected: -rw------- 1 www-data www-data ... .env
Development Environment Variables
Location: .env (in project root, git-ignored)
Template: .env.example
# Development configuration (localhost)
DATABASE_URL=postgresql://nordabiz_app:NordaBiz2025Secure@127.0.0.1:5433/nordabiz
APP_URL=http://localhost:5000
FLASK_ENV=development
Note: Development uses Docker PostgreSQL on port 5433 (not 5432)
Database Configuration
Production Database
Server: NORDABIZ-01 (10.22.68.249)
DBMS: PostgreSQL 14
Database Name: nordabiz
Port: 5432 (localhost only)
Database Users and Roles
| User | Role | Permissions | Purpose |
|---|---|---|---|
postgres |
Superuser | ALL | Database administration |
nordabiz_app |
Application user | CRUD on all tables | Flask application |
nordabiz_readonly |
Read-only | SELECT only | Reporting, backups |
Connection Strings
Production (Flask app):
postgresql://nordabiz_app:<password>@127.0.0.1:5432/nordabiz
Development (Docker):
postgresql://nordabiz_app:NordaBiz2025Secure@127.0.0.1:5433/nordabiz
Direct psql (production):
# As application user
sudo -u www-data psql -U nordabiz_app -d nordabiz -h 127.0.0.1
# As postgres superuser
sudo -u postgres psql nordabiz
PostgreSQL Configuration Files
Location: /etc/postgresql/14/main/
postgresql.conf (Key Settings):
# Network Settings
listen_addresses = 'localhost' # ⚠️ CRITICAL: localhost only!
port = 5432
# Memory Settings
shared_buffers = 256MB
effective_cache_size = 1GB
work_mem = 16MB
# Connection Settings
max_connections = 100
# Logging
log_destination = 'stderr'
logging_collector = on
log_directory = '/var/log/postgresql'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_statement = 'mod' # Log all modifications
log_min_duration_statement = 1000 # Log slow queries (>1s)
pg_hba.conf (Access Control):
# TYPE DATABASE USER ADDRESS METHOD
# Local connections
local all postgres peer
local nordabiz nordabiz_app md5
# IPv4 local connections
host nordabiz nordabiz_app 127.0.0.1/32 md5
# ⚠️ CRITICAL: No external connections allowed!
# External access is blocked by listening on localhost only
Database Backup Configuration
Backup Location: /var/backups/nordabiz/
Owner: postgres:postgres
Retention: 7 days
Daily Backup Cron:
# /etc/cron.d/nordabiz-backup
0 2 * * * postgres pg_dump -U nordabiz_app nordabiz | gzip > /var/backups/nordabiz/nordabiz_$(date +\%Y\%m\%d).sql.gz
Manual Backup:
# Create backup
sudo -u postgres pg_dump -U nordabiz_app nordabiz > /tmp/nordabiz_backup_$(date +%Y%m%d_%H%M%S).sql
# Create compressed backup
sudo -u postgres pg_dump -U nordabiz_app nordabiz | gzip > /tmp/nordabiz_backup.sql.gz
# Restore from backup
sudo -u postgres psql -U nordabiz_app nordabiz < /tmp/nordabiz_backup.sql
Gunicorn WSGI Configuration
Gunicorn Settings
Location: Configured in systemd service file
Socket: 0.0.0.0:5000
Workers: 4 (recommended: 2-4 x CPU cores)
Worker Class: sync (default)
Timeout: 120 seconds
Max Requests: 1000 (worker restart after 1000 requests)
Access Log: /var/log/nordabiznes/gunicorn_access.log
Error Log: /var/log/nordabiznes/gunicorn_error.log
Gunicorn Command
gunicorn \
--bind 0.0.0.0:5000 \
--workers 4 \
--timeout 120 \
--max-requests 1000 \
--access-logfile /var/log/nordabiznes/gunicorn_access.log \
--error-logfile /var/log/nordabiznes/gunicorn_error.log \
--log-level info \
app:app
Worker Calculation
# Recommended workers formula
workers = (2 * num_cpu_cores) + 1
# For NORDABIZ-01 (4 vCPUs)
workers = (2 * 4) + 1 = 9 # Maximum
workers = 4 # Current (conservative)
Gunicorn Tuning Considerations
| Parameter | Current | Tuning Notes |
|---|---|---|
| Workers | 4 | Increase to 6-8 under high load |
| Timeout | 120s | Sufficient for AI chat (Gemini API ~2-5s) |
| Max Requests | 1000 | Prevents memory leaks |
| Worker Class | sync | Consider gevent for WebSocket support |
| Keep-Alive | 5s | Default, no tuning needed |
Systemd Service Configuration
Service File Location
File: /etc/systemd/system/nordabiznes.service
Owner: root:root
Permissions: 0644
Complete Service Configuration
[Unit]
Description=Norda Biznes Partner - Flask Application
After=network.target postgresql.service
Requires=postgresql.service
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/var/www/nordabiznes
Environment="PATH=/var/www/nordabiznes/venv/bin"
EnvironmentFile=/var/www/nordabiznes/.env
ExecStart=/var/www/nordabiznes/venv/bin/gunicorn \
--bind 0.0.0.0:5000 \
--workers 4 \
--timeout 120 \
--max-requests 1000 \
--access-logfile /var/log/nordabiznes/gunicorn_access.log \
--error-logfile /var/log/nordabiznes/gunicorn_error.log \
--log-level info \
app:app
# Restart policy
Restart=always
RestartSec=10
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=nordabiznes
# Security
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Service Management Commands
# Start service
sudo systemctl start nordabiznes
# Stop service
sudo systemctl stop nordabiznes
# Restart service (after code changes)
sudo systemctl restart nordabiznes
# Reload service (graceful restart)
sudo systemctl reload nordabiznes
# Check status
sudo systemctl status nordabiznes
# Enable autostart on boot
sudo systemctl enable nordabiznes
# Disable autostart
sudo systemctl disable nordabiznes
# View logs
sudo journalctl -u nordabiznes -f
# View logs (last 100 lines)
sudo journalctl -u nordabiznes -n 100
# View logs (since 1 hour ago)
sudo journalctl -u nordabiznes --since "1 hour ago"
Service Restart After Changes
# After modifying .env file
sudo systemctl restart nordabiznes
# After modifying Python code
sudo systemctl restart nordabiznes
# After modifying systemd service file
sudo systemctl daemon-reload
sudo systemctl restart nordabiznes
# After database schema changes
# (Usually no restart needed, but recommended)
sudo systemctl restart nordabiznes
Git Repository Configuration
Git Remotes
| Remote | URL | Purpose |
|---|---|---|
| inpi (primary) | https://10.22.68.180:3000/maciejpi/nordabiz.git |
Internal Gitea (deployment source) |
| origin | git@github.com:pienczyn/nordabiz.git |
GitHub (cloud backup) |
Production Git Configuration
Repository Location: /var/www/nordabiznes/
User: www-data
Branch: master
Git Configuration (/var/www/nordabiznes/.git/config):
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[remote "inpi"]
url = https://10.22.68.180:3000/maciejpi/nordabiz.git
fetch = +refs/heads/*:refs/remotes/inpi/*
[remote "origin"]
url = git@github.com:pienczyn/nordabiz.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = inpi
merge = refs/heads/master
[http]
sslVerify = false # Required for self-signed Gitea cert
Gitea Server Configuration
Server: r11-git-inpi
IP: 10.22.68.180
Port: 3000 (HTTPS)
URL: https://10.22.68.180:3000/
User: maciejpi
Repository: maciejpi/nordabiz
Deployment Workflow
# On development machine (Mac)
git push inpi master # Push to internal Gitea
git push origin master # Backup to GitHub (optional)
# On production server (NORDABIZ-01)
ssh maciejpi@10.22.68.249
cd /var/www/nordabiznes
sudo -u www-data git pull # Pull from Gitea
sudo systemctl restart nordabiznes # Restart application
curl -I https://nordabiznes.pl/health # Verify deployment
Git Commands for Production
# Check current branch and status
sudo -u www-data git branch
sudo -u www-data git status
# Pull latest changes
sudo -u www-data git pull
# View commit history
sudo -u www-data git log --oneline -10
# Rollback to previous commit (emergency)
sudo -u www-data git reset --hard HEAD~1
# Force pull (discard local changes)
sudo -u www-data git fetch --all
sudo -u www-data git reset --hard inpi/master
SSH Keys for Git Access
Production Server:
- Location:
/home/www-data/.ssh/ - Public key:
id_rsa.pub - Private key:
id_rsa - Known hosts:
known_hosts(includes Gitea fingerprint)
Development Machine:
- GitHub SSH key:
~/.ssh/id_rsa(GitHub account:pienczyn) - Gitea HTTP access: Username/password (no SSH key required due to HTTPS)
Critical File Paths
Application Files
| Path | Description | Owner | Permissions |
|---|---|---|---|
/var/www/nordabiznes/ |
Application root directory | www-data:www-data | 0755 |
/var/www/nordabiznes/app.py |
Main Flask application | www-data:www-data | 0644 |
/var/www/nordabiznes/.env |
Environment variables (secrets) | www-data:www-data | 0600 |
/var/www/nordabiznes/venv/ |
Python virtual environment | www-data:www-data | 0755 |
/var/www/nordabiznes/static/ |
Static assets (CSS, JS, images) | www-data:www-data | 0755 |
/var/www/nordabiznes/templates/ |
Jinja2 templates | www-data:www-data | 0755 |
/var/www/nordabiznes/database.py |
SQLAlchemy models | www-data:www-data | 0644 |
/var/www/nordabiznes/scripts/ |
Background scripts | www-data:www-data | 0755 |
Configuration Files
| Path | Description | Owner | Permissions |
|---|---|---|---|
/etc/systemd/system/nordabiznes.service |
Systemd service file | root:root | 0644 |
/etc/postgresql/14/main/postgresql.conf |
PostgreSQL configuration | postgres:postgres | 0644 |
/etc/postgresql/14/main/pg_hba.conf |
PostgreSQL access control | postgres:postgres | 0640 |
Log Files
| Path | Description | Rotation | Owner |
|---|---|---|---|
/var/log/nordabiznes/gunicorn_access.log |
Gunicorn access log | Daily | www-data |
/var/log/nordabiznes/gunicorn_error.log |
Gunicorn error log | Daily | www-data |
/var/log/postgresql/postgresql-*.log |
PostgreSQL logs | Weekly | postgres |
journalctl -u nordabiznes |
Systemd service logs | 30 days | root |
Backup Locations
| Path | Description | Retention | Owner |
|---|---|---|---|
/var/backups/nordabiz/ |
Daily database backups | 7 days | postgres |
/home/maciejpi/backups/ |
Manual backups | Manual | maciejpi |
Temporary Files
| Path | Description | Cleanup | Owner |
|---|---|---|---|
/tmp/ |
Temporary files (uploads, etc.) | Reboot | Various |
/var/tmp/ |
Persistent temp files | 30 days | Various |
Firewall and Network Rules
Fortigate Firewall Configuration
WAN Interface: wan1 (85.237.177.83)
LAN Interface: internal (10.22.68.1)
NAT Rules (Destination NAT / Port Forwarding):
Rule 1: HTTPS
External: 85.237.177.83:443 → Internal: 10.22.68.250:443
Protocol: TCP
Action: DNAT
Rule 2: HTTP
External: 85.237.177.83:80 → Internal: 10.22.68.250:80
Protocol: TCP
Action: DNAT
Firewall Policies:
Policy 1: Allow HTTPS from Internet
Source: all
Destination: 10.22.68.250
Service: HTTPS (443)
Action: ACCEPT
Policy 2: Allow HTTP from Internet
Source: all
Destination: 10.22.68.250
Service: HTTP (80)
Action: ACCEPT
Policy 3: Allow SSH from Admin Network
Source: admin_network (10.22.68.0/24)
Destination: all_internal_servers
Service: SSH (22)
Action: ACCEPT
Policy 4: Allow Internal Traffic
Source: 10.22.68.0/24
Destination: 10.22.68.0/24
Service: any
Action: ACCEPT
Policy 5: Default Deny
Source: all
Destination: all
Service: any
Action: DENY
Linux iptables (NORDABIZ-01)
Status: Not actively used (relies on Fortigate firewall)
Default Policy:
# Check iptables status
sudo iptables -L -n -v
# Expected: Mostly ACCEPT policies (Fortigate handles filtering)
PostgreSQL Access Control
Network Access: listen_addresses = 'localhost' (127.0.0.1 only)
pg_hba.conf Rules:
# Only allow localhost connections
host nordabiz nordabiz_app 127.0.0.1/32 md5
# Block all other addresses (implicit)
Backup and Recovery Locations
Database Backups
Automated Daily Backups:
- Location:
/var/backups/nordabiz/ - Filename Pattern:
nordabiz_YYYYMMDD.sql.gz - Schedule: 2:00 AM daily (cron)
- Retention: 7 days (automatic cleanup)
- Size: ~5-10 MB compressed
Manual Backups:
# Create manual backup
sudo -u postgres pg_dump -U nordabiz_app nordabiz > \
/home/maciejpi/backups/nordabiz_manual_$(date +%Y%m%d_%H%M%S).sql
# Restore from backup
sudo -u postgres psql -U nordabiz_app nordabiz < \
/home/maciejpi/backups/nordabiz_manual_20260110_120000.sql
Application Backups
Git Repository:
- Primary: Gitea (10.22.68.180) - internal backup
- Secondary: GitHub (github.com/pienczyn/nordabiz) - cloud backup
VM Snapshots (Proxmox):
- Location: Proxmox Backup Server
- Schedule: Weekly
- Retention: 4 weeks
- VM ID: 249 (NORDABIZ-01)
Configuration Backups
NPM Configuration:
# Backup NPM database
ssh maciejpi@10.22.68.250 \
"docker cp nginx-proxy-manager_app_1:/data/database.sqlite /tmp/npm_backup.sqlite"
# Copy to local
scp maciejpi@10.22.68.250:/tmp/npm_backup.sqlite ~/backups/
Environment Variables:
# Backup .env file (contains secrets!)
sudo cp /var/www/nordabiznes/.env /home/maciejpi/backups/.env.backup
sudo chmod 600 /home/maciejpi/backups/.env.backup
Disaster Recovery Procedure
Complete Server Rebuild:
- Restore VM from Proxmox snapshot (or provision new VM)
- Install base packages:
sudo apt update sudo apt install -y python3 python3-venv python3-pip postgresql-14 git nginx - Restore database:
sudo -u postgres createdb nordabiz sudo -u postgres psql nordabiz < /path/to/backup.sql - Clone application:
sudo mkdir -p /var/www/nordabiznes sudo chown www-data:www-data /var/www/nordabiznes sudo -u www-data git clone https://10.22.68.180:3000/maciejpi/nordabiz.git /var/www/nordabiznes - Restore .env file:
sudo cp /path/to/.env.backup /var/www/nordabiznes/.env sudo chown www-data:www-data /var/www/nordabiznes/.env sudo chmod 600 /var/www/nordabiznes/.env - Install Python dependencies:
cd /var/www/nordabiznes sudo -u www-data python3 -m venv venv sudo -u www-data venv/bin/pip install -r requirements.txt - Configure systemd service:
sudo cp nordabiznes.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable nordabiznes sudo systemctl start nordabiznes - Reconfigure NPM:
- Update proxy host 27 to point to new server IP
- Verify
forward_port = 5000
- Verify deployment:
curl -I https://nordabiznes.pl/health # Expected: HTTP/2 200 OK
Configuration Management
Configuration Change Procedure
⚠️ ALWAYS follow this procedure for production changes:
-
Backup current configuration
# Database sudo -u postgres pg_dump nordabiz > /tmp/backup_before_change.sql # Application cd /var/www/nordabiznes sudo -u www-data git status # Ensure clean state -
Test changes in development
# On local machine # Test with Docker PostgreSQL on port 5433 python3 app.py # Verify functionality -
Document the change
- Update CLAUDE.md if architecture changes
- Update this document if configuration changes
- Add entry to CHANGELOG.md
-
Apply change to production
# SSH to production ssh maciejpi@10.22.68.249 # Pull changes cd /var/www/nordabiznes sudo -u www-data git pull # Restart service sudo systemctl restart nordabiznes -
Verify change
# Check service status sudo systemctl status nordabiznes # Check application health curl -I https://nordabiznes.pl/health # Expected: HTTP/2 200 OK # Monitor logs sudo journalctl -u nordabiznes -f -
Rollback procedure (if needed)
# Stop service sudo systemctl stop nordabiznes # Rollback code sudo -u www-data git reset --hard HEAD~1 # Rollback database (if needed) sudo -u postgres psql nordabiz < /tmp/backup_before_change.sql # Start service sudo systemctl start nordabiznes
Configuration Version Control
Tracked in Git:
- Application code (
app.py,*.py) - Templates (
templates/) - Static assets (
static/) - Requirements (
requirements.txt) - Documentation (
docs/,CLAUDE.md)
NOT tracked in Git (secrets):
.env(environment variables)*.pyc(Python bytecode)venv/(virtual environment)__pycache__/(Python cache)- Log files
Tracked externally (infrastructure as code):
- NPM configuration (stored in NPM SQLite database)
- Systemd service files (manual versioning)
- PostgreSQL configuration (manual versioning)
Verification Checklist
Pre-Deployment Verification
- Code changes tested in development environment
- Database migrations tested (if applicable)
.envvariables updated (if needed)- No secrets committed to Git
- CHANGELOG.md updated
- Documentation updated (if architecture changed)
Post-Deployment Verification
- NPM configuration verified:
forward_port = 5000⚠️ CRITICAL - Website accessible:
curl -I https://nordabiznes.pl/healthreturns 200 - SSL certificate valid: No browser warnings
- Database connection working: Query returns data
- Service running:
systemctl status nordabiznesshows active - No errors in logs:
journalctl -u nordabiznes -n 50 - External APIs working: Test chat, SEO audit
- Authentication working: Test login/logout
- Static assets loading: Check browser console for 404s
NPM Configuration Verification (CRITICAL!)
# ⚠️ ALWAYS run this after ANY NPM changes!
# 1. Check proxy host configuration
ssh maciejpi@10.22.68.250 "docker exec nginx-proxy-manager_app_1 \
sqlite3 /data/database.sqlite \
\"SELECT id, domain_names, forward_host, forward_port FROM proxy_host WHERE id = 27;\""
# Expected output:
# 27|["nordabiznes.pl","www.nordabiznes.pl"]|10.22.68.249|5000
# ^^^^
# MUST BE 5000!
# 2. Test website accessibility
curl -I https://nordabiznes.pl/health
# Expected: HTTP/2 200 OK
# 3. Test from external network (use phone without WiFi)
# Browse to: https://nordabiznes.pl
# Should load without ERR_TOO_MANY_REDIRECTS
Database Verification
# Check database connectivity
sudo -u www-data psql -U nordabiz_app -d nordabiz -h 127.0.0.1 -c "SELECT COUNT(*) FROM companies;"
# Check recent data
sudo -u www-data psql -U nordabiz_app -d nordabiz -h 127.0.0.1 -c \
"SELECT name, slug FROM companies ORDER BY created_at DESC LIMIT 5;"
# Check database size
sudo -u postgres psql -c "SELECT pg_size_pretty(pg_database_size('nordabiz'));"
Service Health Verification
# Systemd service status
sudo systemctl status nordabiznes
# Process check
ps aux | grep gunicorn
# Port listening check
sudo netstat -tlnp | grep 5000
# Expected: gunicorn listening on 0.0.0.0:5000
# Application logs (last 50 lines)
sudo journalctl -u nordabiznes -n 50 --no-pager
# Error log check (should be minimal)
sudo journalctl -u nordabiznes -p err -n 20
Emergency Contacts and Resources
Server Access
| Server | IP | SSH User | Purpose |
|---|---|---|---|
| NORDABIZ-01 | 10.22.68.249 | maciejpi |
Application server |
| R11-REVPROXY-01 | 10.22.68.250 | maciejpi |
NPM proxy |
| r11-git-inpi | 10.22.68.180 | maciejpi |
Gitea repository |
⚠️ NEVER SSH as root! Always use maciejpi user and sudo for elevated privileges.
Key Commands for Incidents
# 1. Quick health check
curl -I https://nordabiznes.pl/health
# 2. Check service status
ssh maciejpi@10.22.68.249 "sudo systemctl status nordabiznes"
# 3. Check NPM proxy configuration
ssh maciejpi@10.22.68.250 "docker exec nginx-proxy-manager_app_1 \
sqlite3 /data/database.sqlite \
\"SELECT forward_port FROM proxy_host WHERE id = 27;\""
# 4. View recent errors
ssh maciejpi@10.22.68.249 "sudo journalctl -u nordabiznes -p err -n 20"
# 5. Restart application
ssh maciejpi@10.22.68.249 "sudo systemctl restart nordabiznes"
Documentation Resources
- This Document:
docs/architecture/08-critical-configurations.md - Incident Report:
docs/INCIDENT_REPORT_20260102.md - Deployment Architecture:
docs/architecture/03-deployment-architecture.md - Network Topology:
docs/architecture/07-network-topology.md - HTTP Request Flow:
docs/architecture/flows/06-http-request-flow.md - Main Documentation:
CLAUDE.md
Appendix: Quick Reference Cards
Quick Reference: NPM Proxy
Proxy Host ID: 27
Domains: nordabiznes.pl, www.nordabiznes.pl
Backend: 10.22.68.249:5000 ⚠️ PORT 5000 (NOT 80!)
SSL: Let's Encrypt (auto-renew)
Quick Reference: Port Mappings
Internet → Fortigate → NPM → Flask
:443 → :443 → :5000
⚠️ CRITICAL: NPM → Flask must use port 5000
Quick Reference: Database
Host: 127.0.0.1 (localhost only)
Port: 5432
Database: nordabiz
User: nordabiz_app
Quick Reference: Service Management
sudo systemctl restart nordabiznes # Restart app
sudo journalctl -u nordabiznes -f # View logs
curl -I https://nordabiznes.pl/health # Test health
Quick Reference: Emergency Rollback
ssh maciejpi@10.22.68.249
cd /var/www/nordabiznes
sudo systemctl stop nordabiznes
sudo -u www-data git reset --hard HEAD~1
sudo systemctl start nordabiznes
curl -I https://nordabiznes.pl/health
Document Maintenance:
- Update this document after ANY configuration changes
- Verify accuracy quarterly
- Include in onboarding materials for new team members
- Reference in incident response procedures
Last Verified: 2026-01-10 Next Review: 2026-04-10