Production Linux Deployment Guide: Where to Put Your Files
A practical guide to file placement on production Linux servers following the Filesystem Hierarchy Standard (FHS). Learn where to place applications, configs, logs, and data for consistent and maintainable deployments.
Production Linux Deployment Guide: Where to Put Your Files
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 guide 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.