Production Linux Deployment Guide: Where to Put Your Files
On this page
- Why File Placement Matters
- Quick Reference: Standard Directory Mapping
- Decision Matrices
- When to use /opt/app-name/ vs /usr/local/
- When to use /srv/ vs /var/www/
- Configuration File Precedence
- Common Application Types
- Web Applications (Node.js, Python, Ruby, etc.)
- Database Services
- Monitoring Tools (Prometheus, Grafana, etc.)
- Security Standards
- Mandatory Permission Requirements
- Firewall Best Practices
- Maintenance Essentials
- Backup Strategy
- Log Rotation
- Common Issues & Quick Fixes
- Quick Diagnostics
- Deployment Verification Checklist
- Quick Command Reference
- Key Takeaways
Why File Placement Matters
When deploying applications to production Linux servers, knowing where to put files isn't just about organization—it's about:
- Consistency across your infrastructure and team workflows
- Predictable maintenance, backup, and troubleshooting procedures
- Compliance with the Filesystem Hierarchy Standard (FHS) 3.0
- Portability across different Linux distributions
This blog covers production deployments for Ubuntu, Debian, RHEL, and CentOS systems.
Quick Reference: Standard Directory Mapping
| Asset Type | Standard Location | Owner | Permissions | Backup Priority |
|---|---|---|---|---|
| Application binaries/code | /opt/app-name/ | app-user | 0755 | Medium |
| System configuration | /etc/app-name/ | root | 0644 | Critical |
| Sensitive credentials | /etc/app-name/secrets/ | root | 0600 | Critical |
| Persistent data | /var/lib/app-name/ | app-user | 0750 | Critical |
| Application logs | /var/log/app-name/ | app-user | 0755 | Low |
| Cache/temporary data | /var/cache/app-name/ | app-user | 0755 | None |
| Static web assets | /srv/app-name/ | www-data | 0755 | Medium |
| Maintenance scripts | /usr/local/sbin/ | root | 0750 | Medium |
Decision Matrices
When to use /opt/app-name/ vs /usr/local/
Use /opt/app-name/ for:
- Third-party software packages
- Vendor-provided applications
- Self-contained applications with multiple components
- Applications managed independently from system packages
Use /usr/local/ for:
- Locally compiled tools and utilities
- Custom scripts used system-wide
- Override or supplement system binaries
- Single-binary applications
When to use /srv/ vs /var/www/
Use /srv/service/ for:
- Service-specific data (FTP servers, Git repositories)
- Application-specific static content
- Multi-service environments
Use /var/www/ for:
- Traditional web server document roots
- Nginx/Apache default configurations
- Simple static website hosting
Configuration File Precedence
Applications should check configuration in this order:
/etc/app-name/- System-wide configuration (highest priority)/opt/app-name/etc/- Application-bundled defaults- Environment variables - Runtime overrides
Common Application Types
Web Applications (Node.js, Python, Ruby, etc.)
Standard Structure:
/opt/myapp/ # Application code
/etc/myapp/ # Configuration files
└── secrets/ # Sensitive credentials (0600)
/var/lib/myapp/ # Persistent data (uploads, sessions)
/var/log/myapp/ # Application logs
/var/cache/myapp/ # Cache dataDatabase Services
Standard locations for common databases:
| Database | Data Directory | Config File | Log Directory |
|---|---|---|---|
| PostgreSQL | /var/lib/postgresql/ | /etc/postgresql/*/main/postgresql.conf | /var/log/postgresql/ |
| MySQL/MariaDB | /var/lib/mysql/ | /etc/mysql/my.cnf | /var/log/mysql/ |
| MongoDB | /var/lib/mongodb/ | /etc/mongod.conf | /var/log/mongodb/ |
| Redis | /var/lib/redis/ | /etc/redis/redis.conf | /var/log/redis/ |
Note: Package managers (apt/yum) automatically create FHS-compliant structures. Preserve these conventions for consistency.
Monitoring Tools (Prometheus, Grafana, etc.)
/opt/tool/ # Binary and static assets
/etc/tool/ # Configuration files
/var/lib/tool/ # Persistent data (databases, dashboards)
/var/log/tool/ # Application logsSecurity Standards
Mandatory Permission Requirements
# Application directories
sudo chmod 0755 /opt/app/
sudo chmod 0750 /var/lib/app/
sudo chmod 0755 /var/log/app/
# Configuration files
sudo chmod 0644 /etc/app/*.{yaml,conf,ini}
sudo chmod 0600 /etc/app/secrets/*
sudo chown root:root /etc/app/secrets/*
# Verify with namei
namei -l /etc/app/secrets/credentials.jsonFirewall Best Practices
# Only expose necessary ports
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw allow 22/tcp # SSH
# NEVER expose application ports directly
# Always use reverse proxy (Nginx/Apache)Maintenance Essentials
Backup Strategy
Daily Backups (Critical):
/var/lib/app/→ Application state and databases/etc/app/→ Configuration files and secrets
Weekly Backups (Important):
/opt/app/→ Application code (usually redundant with Git)
No Backup Required:
/var/cache/app/→ Regeneratable cache/var/log/app/→ Archive separately, don't restore/tmp/→ Temporary files
Backup Script Location: /usr/local/sbin/backup-app.sh
Log Rotation
Create /etc/logrotate.d/app:
/var/log/app/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
create 0640 app-user app-user
sharedscripts
postrotate
systemctl reload app > /dev/null 2>&1 || true
endscript
}Test configuration:
sudo logrotate -d /etc/logrotate.d/app # Dry run
sudo logrotate -f /etc/logrotate.d/app # Force rotationCommon Issues & Quick Fixes
| Issue | Symptom | Root Cause | Resolution |
|---|---|---|---|
| Permission denied | App can't write to data directory | Wrong ownership | sudo chown -R user:group /var/lib/app/ |
| Config not found | App fails to load configuration | Incorrect path in systemd | Verify Environment=CONFIG_PATH=/etc/app/config.yaml |
| Disk full | /var partition at 100% | Log/cache accumulation | du -sh /var/log/* /var/cache/*, run logrotate |
| Service won't start | Systemd activation fails | Missing dependencies or permissions | journalctl -u app.service -n 50 |
| SELinux blocking | AVC denial messages | Incorrect security context | ausearch -m avc -ts recent, add policy |
| Port conflict | Address already in use | Multiple apps on same port | `sudo ss -tlnp |
Quick Diagnostics
# Check service status
systemctl status app
# View recent logs
journalctl -u app -n 50 --no-pager
# Check disk usage
df -h
du -sh /var/lib/app /var/log/app /var/cache/app
# Verify permissions
namei -l /etc/app/secrets/credentials.json
ls -la /var/lib/app/
# Check listening ports
sudo ss -tlnp | grep app
# SELinux troubleshooting
sudo ausearch -m avc -ts recentDeployment Verification Checklist
Before marking any deployment as complete, verify:
# ✓ Directory structure exists
ls -la /opt/app/ /etc/app/ /var/lib/app/ /var/log/app/
# ✓ Ownership is correct
stat -c '%U:%G' /opt/app/
stat -c '%U:%G' /var/lib/app/
# ✓ Permissions are secure
namei -l /etc/app/secrets/credentials.json
find /etc/app/secrets/ -type f ! -perm 0600 -ls
# ✓ Systemd service is enabled and running
systemctl is-enabled app
systemctl is-active app
# ✓ Application is responding
curl http://localhost:port/health
# ✓ Logs are generating
ls -lh /var/log/app/
# ✓ Log rotation configured
test -f /etc/logrotate.d/app && echo "OK" || echo "MISSING"
# ✓ Backup script exists
test -x /usr/local/sbin/backup-app.sh && echo "OK" || echo "MISSING"Quick Command Reference
Create standard directory structure for any application:
# Create standard app structure
create-app-structure() {
APP=$1
sudo useradd -r -s /bin/false "$APP"
sudo mkdir -p /opt/"$APP" /etc/"$APP"/secrets /var/{lib,log,cache}/"$APP"
sudo chown -R "$APP":"$APP" /opt/"$APP" /var/{lib,log,cache}/"$APP"
sudo chown -R root:root /etc/"$APP"
sudo chmod 700 /etc/"$APP"/secrets
}
# Usage: create-app-structure myappKey Takeaways
- Consistency is crucial - Always follow FHS standards for predictable operations
- Separate concerns - Keep code, config, data, and logs in their designated locations
- Security first - Protect secrets with proper permissions (0600) and ownership (root)
- Plan for backups - Critical data goes in
/var/lib/app/and/etc/app/ - Document everything - Use systemd service files and maintain clear documentation
Remember: Following these standards ensures predictable operations, easier troubleshooting, and smoother team collaboration. When in doubt, stick to the FHS conventions—they've been battle-tested across countless production deployments.