Whether you're managing servers, writing scripts, or troubleshooting complex systems, one of the most valuable tools at your disposal is your command history. But the default Bash history has serious limitations: it’s easy to lose, doesn't timestamp by default, and doesn't log everything in real time.
What if you could keep a permanent, timestamped, real-time log of every command you ever run in Bash?
Good news: you can.
In this guide, we’ll walk through how to set up robust, automatic Bash logging to track every command you type—across sessions, with full timestamps, and even with user and host information. Ideal for system administrators, developers, auditors, or anyone who wants to maintain a clear, searchable audit trail.
Why Bash Logging Persistence So Important ?
Before we dive into the how, let's understand the why:
- Accountability – Know exactly what commands were run, by whom, and when.
- Auditability – Great for security reviews or compliance requirements.
- Troubleshooting – Trace back actions that caused issues.
- Documentation – Reuse commands or share with teammates.
- Forensics – Investigate suspicious activity.
How Bash History Behaves ( By Default )
Without any config everyone knows , Bash uses a file called ~/.bash_history in $HOME to save command history.
What is tricky here:
- .bash_history not written to immediately – only when the session exits.
- It can be overwritten by other sessions.
- It lacks timestamps unless explicitly configured.
- It doesn’t log failed attempts or commands from other users.
In this short article I'll show you one of the ways on how to make .bash_history keeps the record for you even though some user tries to hide tihngs by running the commands and exiting the shell abnormally by killing it with the well known command by hackers and sysadmin gurus:
$ kill -9 $$
The command forces the user you have logged into to kill the process of the bash (-bash).
Here is how.
Enable Advanced Bash Logging
1. Enable Timestamps in History
Add this line to your ~/.bashrc or ~/.bash_profile:
export HISTTIMEFORMAT="%F %T "
This formats the date/time as YYYY-MM-DD HH:MM:SS.
After modifying the file, run:
source ~/.bashrc
Now, run:
history
And you’ll see timestamps next to your commands.
2. Increase History Size
The default history size is often too small. Let’s increase it:
export HISTSIZE=100000
export HISTFILESIZE=200000
Add these to ~/.bashrc as well.
3. Log Commands Immediately (Across Sessions)
By default, Bash only writes history when the shell exits. To log commands in real time, add the following to ~/.bashrc:
# Append to the history file, don't overwrite it
shopt -s histappend
# Immediately append command to history file after execution
PROMPT_COMMAND='history -a; history -n'
Explanation:
- history -a: Append current session's command to ~/.bash_history
- history -n: Read any new lines from the file (from other sessions)
4. Log All Commands to a Separate File (for each User)
To keep a separate, detailed log, you can use the trap command in combination with logger, or write to a custom file.
Add this to your ~/.bashrc:
LOG_FILE="$HOME/.bash_command_log"
trap 'echo "$(date "+%F %T") | $(whoami)@$(hostname) | $(pwd) | $BASH_COMMAND" >> "$LOG_FILE"' DEBUG
This logs every command as for example:
2025-10-10 14:25:02 | master_app@server01 | /var/www | systemctl restart nginx
This file can grow large over time – consider rotating it regularly with logrotate or similar tools.
To prevent the file 100% from being modified by the user itself you can make the log file immutable with command
# chattr +i $HOME/.bash_command_log
5. Guarantee log security, Make copy of Logs to prevent hackers to modify them
If logging for audit/security purposes:
- Store logs in append-only files (chattr +a logfile on ext4 FS)
- store files with rsyslog service (see below)
- Use remote logging (e.g., send via logger to syslog / rsyslog or any other centralized logging service) / logcollector etc.
- Monitor for tampering or suspicious gaps
6. Store file with rsyslog service
Create the file and set it proper permissions
# touch /var/log/bash_audit.log
# chmod 600 /var/log/bash_audit.log
# chown root:root /var/log/bash_audit.log
# vim /etc/rsyslog.d/bash_audit.conf
Add:
if $programname == 'bash_audit' then /var/log/bash_audit.log
& stop
# systemctl restart rsyslog
To later verify it works fine
# tail -f /var/log/bash_audit.log
# journalctl -t bash_audit
6. Add Global Bash Logging for All Users
Assuming that the bash_audit set program / name tag is already done as in step 5.
To apply logging system-wide, Edit /etc/profile /etc/bash_profile or /etc/bash.bashrc and include the same trap cmd and logging is ready. Ensure:
- The log file is writable by users (or add users to a group that can append to file) or modify the command to use sudo logger for centralized syslog.
- You test it carefully before deploying to all users.
An improved wide user version of trap command would be something like this
# Bash command logging (readable layer)
trap 'CMD=$(history 1 | sed "s/^[ ]*[0-9]+[ ]*//");
MSG="$(date "+%F %T") | $(whoami)@$(hostname) | $(pwd) | $CMD";/usr/bin/logger -t bash_audit "$MSG"
' DEBUG
Make these two env variables read only for additional hardening
readonly PROMPT_COMMAND
readonly HISTFILE
Note that you will need to edit passwordless login for sudo to logger
- Setup auditd to make file read only
# apt install auditd audispd-plugins –yes
- Test it with auditctl
# auditctl -a always,exit -F arch=b64 -S execve -F auid>=1000 -F auid!=4294967295 -k cmdlog
# auditctl -a always,exit -F arch=b32 -S execve -F auid>=1000 -F auid!=4294967295 -k cmdlog
- Make rules permanent via cmdlog.rules
# vim /etc/audit/rules.d/cmdlog.rules
-a always,exit -F arch=b64 -S execve -F auid>=1000 -F auid!=4294967295 -k cmdlog
-a always,exit -F arch=b32 -S execve -F auid>=1000 -F auid!=4294967295 -k cmdlog
- Load and lock audit rules
# augenrules –load
# auditctl -e 2
- Check audit logs
# ausearch -k cmdlog -i
exe="/usr/bin/ls" argc=1 a0="ls"
7. Rotate Log Files Automatically with logrotate
Create a logrotate config like /etc/logrotate.d/bash_command_log:
/home/*/.bash_command_log {
daily
rotate 7
compress
missingok
notifempty
}
/var/log/bash_audit.log {
daily
rotate 7
compress
missingok
notifempty
}
This keeps logs for 7 days and compresses old ones.
8. Test Every command Logging is permanenty stored
After setting bash logging up up:
- Open a new terminal client with SSH session
- Run a few commands
- Check ~/.bash_command_log (or your alternative configured log location)
You should see a real-time record of every command executed.
Use tools like grep, awk, or fzf Command fuzzy finder to search through your command log efficiently. Example:
grep apt ~/.bash_command_log
You can further automate it and deploy it to multiple servers with Ansible or some shell scripting.
If you need it Ask me how to automate it?
Ask me how to automate it with Ansible or a shell script.
Wrapping it Up
With just a few lines in Bash config, basic history feature becomes a persistent, and timestamped static record that’s invaluable for system admins, developers, and security teams.
Summary Checklist
- Enable HISTTIMEFORMAT
- Increase history size
- Append history in real time
- Log every command with trap DEBUG
- Optionally send to rsyslog / syslogd / systemd-journald or other central log server (Fluentd / ELK Stack / Graylog)
- Rotate logs with logrotate
More helpful Articles
Tags: bashrc, command, configured, default, Documentation Reuse, doesn, example, gurus, HISTFILESIZE, HISTTIMEFORMAT, Linux Bash Logging, linux user, Load, logging, Pro Tip, real time, rsyslog, sessions, trap, User, variables







