When working with PostgreSQL on Unix-like systems, both commands achieve similar results but through different mechanisms:
# Traditional approach using sudo+su
sudo su - postgres
# Modern sudo approach
sudo -u postgres -i
The key technical difference lies in environment variable handling:
# sudo su - postgres performs:
1. sudo executes su as root
2. su - loads postgres user's login environment
3. Creates new shell session
# sudo -u postgres -i:
1. Directly executes as postgres user
2. -i flag simulates initial login
3. More efficient process chain
For running psql with proper environment:
# Suboptimal nested privilege elevation
sudo su - postgres -c "psql -c 'SELECT version();'"
# Direct single-command approach
sudo -u postgres psql -c 'SELECT version();'
The sudo+su pattern creates additional process overhead and potential security concerns:
# Process tree for sudo su -
sudo → su → bash → command
# Process tree for sudo -u
sudo → command
Modern Linux systems with properly configured sudoers eliminate the need for su nesting.
For administration tasks:
# Single command execution
sudo -u postgres psql
# Interactive session
sudo -u postgres -i
# Complex command chains
sudo -u postgres bash -c 'command1 && command2'
The sudo -u approach provides better auditing through syslog and leaves cleaner process trees.
Historical reasons for sudo+su usage include:
1. Legacy systems without proper sudo -i support
2. Muscle memory from older Unix admin practices
3. Misunderstanding of environment inheritance
Modern systems (PostgreSQL 9.0+, Linux 2.6+ kernels) handle sudo -u postgres correctly.
If encountering environment issues:
# Compare environments
sudo su - postgres -c 'env | sort' > env_su.txt
sudo -u postgres -i env | sort > env_sudo.txt
diff env_su.txt env_sudo.txt
# Debug command path resolution
sudo -u postgres which psql
When working with PostgreSQL, you'll often need to switch to the postgres
system user account. The database uses peer authentication for local connections by default, meaning your Unix username must match the PostgreSQL username you're trying to use. This leads to two common approaches for switching users:
# Method 1: Using sudo with su
sudo su - postgres
# Method 2: Using sudo's -u flag directly
sudo -u postgres -i
While both commands achieve similar results, they work differently under the hood:
- Environment Handling:
sudo -u postgres -i
creates a login shell with the user's environment, similar tosu -
- Process Hierarchy:
sudo su
creates an additional process (the su command) that isn't needed - Command Execution: The
-c
flag behaves differently between the two approaches
Let's examine common use cases with both methods:
# Running psql with sudo su
sudo su - postgres -c "psql -c 'SELECT version();'"
# Equivalent with direct sudo
sudo -u postgres psql -c 'SELECT version();'
The second method is cleaner as it:
- Doesn't spawn an unnecessary shell process
- Maintains better process tracking in logs
- Has simpler syntax for command execution
There are rare cases where sudo su
might be preferable:
# When you need to chain multiple commands in a shell
sudo su - postgres -c "psql -c 'CREATE DATABASE test'; createdb test2"
# When working with systems where sudo's -i flag isn't available
# (though this is extremely rare on modern systems)
For most cases, I recommend:
# For interactive sessions
sudo -u postgres -i
# For single commands
sudo -u postgres psql
# For multiple commands (better alternative)
sudo -u postgres bash -c 'psql -c "SELECT 1"; psql -c "SELECT 2"'
This approach is more efficient, easier to audit, and follows the principle of using the simplest tool that gets the job done.
Both methods are secure when properly configured, but:
sudo -u
appears cleaner in process listings and logs- Nested privilege escalation (
sudo su
) can sometimes complicate security auditing - Some sudo configurations may restrict shell escapes differently
Always ensure your sudoers file has appropriate restrictions for the postgres user, like:
# /etc/sudoers example
postgres ALL=(postgres) NOPASSWD: ALL