Many developers assume ~/.bashrc
is always sourced, but SSH command execution presents a special case. When you run:
ssh user@host 'command'
The shell operates in non-interactive mode, and most distributions configure bash to skip .bashrc
loading in this scenario.
To test whether your .bashrc
is being sourced during SSH commands:
# Add this to your .bashrc
echo "Bashrc loaded" > /tmp/bashrc_test
# Then test with:
ssh user@host 'cat /tmp/bashrc_test'
# No output means .bashrc wasn't sourced
In Ubuntu/Debian systems, the default .bashrc
starts with this guard clause:
# If not running interactively, don't do anything
case $- in
*i*) ;;
*) return;;
esac
This explains why your touch
command never executed.
Option 1: Force .bashrc loading by modifying your SSH command:
ssh -t user@host 'bash --rcfile ~/.bashrc -ci "your_command"'
Option 2: Create a dedicated environment file (recommended):
# Create ~/.bashenv
cp ~/.bashrc ~/.bashenv
# Remove the interactive guard clause from .bashenv
# Then use:
ssh user@host 'source ~/.bashenv && your_command'
Option 3: For system-wide changes (use with caution):
# Edit /etc/ssh/sshd_config
AcceptEnv BASH_ENV
# Then set BASH_ENV in your local SSH config
- Keep environment setup separate from interactive configurations
- Use absolute paths in your automation scripts
- Test with
-vvv
SSH flag for debugging - Consider using
~/.ssh/environment
for simple variable needs
Here's how to properly structure a deployment command:
ssh deploy@production '
source ~/.deployenv && \
cd /var/www/app && \
git pull && \
npm install && \
systemctl restart appservice
'
Where ~/.deployenv
contains only the necessary environment setup.
When you execute commands via SSH like ssh user@host 'command'
, the remote shell behaves differently from interactive sessions. Here's what actually happens:
# This command creates a non-interactive shell
ssh user@example.com 'ls -l /tmp'
# Check if .bashrc was sourced (it typically isn't)
ssh user@example.com 'echo $BASH_ENV'
Bash has specific rules for which startup files it reads:
- Interactive login shells: Reads /etc/profile, ~/.bash_profile, ~/.bash_login, ~/.profile
- Interactive non-login shells: Reads ~/.bashrc
- Non-interactive shells: Only reads file specified in $BASH_ENV
Here are three reliable approaches:
Option 1: Force .bashrc Loading via BASH_ENV
# In your remote ~/.bash_profile or ~/.profile:
if [ -n "$BASH_VERSION" ]; then
export BASH_ENV=~/.bashrc
fi
Option 2: Use SSH ForcedCommand
# In ~/.ssh/authorized_keys:
command=". ~/.bashrc; $SSH_ORIGINAL_COMMAND" ssh-rsa AAAAB3Nza...
Option 3: Explicitly Source .bashrc
# Modify your SSH command:
ssh user@example.com '. ~/.bashrc; your_command'
To verify your solution works:
# Test script
ssh user@example.com 'set -x; . ~/.bashrc; env > /tmp/ssh_env_test'
ssh user@example.com 'cat /tmp/ssh_env_test'
The behavior might vary slightly between distributions:
# For Ubuntu/Debian systems checking:
if [ -f /etc/debian_version ]; then
# Additional Debian-specific logic
fi
For production environments, I recommend creating a separate environment setup file and using BASH_ENV:
# Create ~/.bash_env
echo 'export PATH=$PATH:/custom/path' > ~/.bash_env
chmod 644 ~/.bash_env
# In ~/.bash_profile
export BASH_ENV=~/.bash_env