This module provides comprehensive auditing capabilities for Valkey servers. It allows logging of various event types to different output protocols and destinations with configurable formatting.
- Multiple logging protocols: file system, syslog support and TCP support
- Configurable formats: text, JSON, and CSV output formats
- Selective event auditing: configure which events to audit
- Command payload options: control whether and how much of a command payloads to include in the audit message
ValkeyAudit uses CMake for building the Valkey module.
mkdir build
cmake -S . -B build
cmake --build build --target allAdd the following line to your Valkey configuration file or issue a MODULE LOAD command. As part of the loadmodule, any of the configuration parameters can be set. Examples:
loadmodule /path/to/libvalkeyaudit.so
loadmodule /path/to/libvalkeyaudit.so audit.protocol "file /var/log/audit/valkey_audit.log"
The module uses the standard Valkey configuration facility which means that parameters can accessed with CONFIG SET and GET as well as written to the valkey.conf file with CONFIG REWRITE or manually.
Available parameters:
audit.enabled [yes|no]: Enable or disable auditing.audit.always_audit_config [yes|no]: Enable or disable the auditing of config commands regardless of per user events setting. This allows the logging of config commands for a user even if the user is in the exclusion list.audit.protocol [file|syslog|tcp]: Logging protocol to use. When using the file protocol it should be followed by the filepath. When using the syslog protocol it should be followed by the syslog facility. When using the tcp protocol it should be followed by the double quoted host:port string.audit.format [text|json|csv]: Log format.audit.events [event1,event2,...]: Event categories to audit (connections,auth,config,keys,other,none,all).audit.command_result_mode [all|failures]: Whether to log all commands (all) or only commands that fail or are rejected (failures). Default isfailures.audit.payload_disable: Disable logging command payloads.audit.payload_maxsize [size]: Maximum payload size to log in bytes.audit.excluderules: Specific usernames and/or IP addresses to exclude from auditing.audit.tcp_host: Hostname or IP for target TCP destination to write audit messages.audit.tcp_port: port destination to write audit messages.audit.tcp_timeout_ms: timeout in ms for establishing TCP connection.audit.tcp_retry_interval_ms: retry interval for establishing TCP connections.audit.tcp_max_retries: maximum number of retries to establish a TCP connection.audit.tcp_buffer_on_disconnect: buffer messages during disconnected state from target TCP destination.audit.tcp_reconnect_on_failure: automatic reconnect to TCP destination on failure.audit.ignore_internal_clients: do not audit internal clients like replication clients
To enable/disable auditing:
CONFIG SET AUDIT.ENABLED yes
CONFIG SET AUDIT.ENABLED no
To enable/disable always auditing of config commands. This will log config commands irrespective of what events are set to be audited:
CONFIG SET AUDIT.ALWAYS_AUDIT_CONFIG yes
CONFIG SET AUDIT.ALWAYS_AUDIT_CONFIG no
To set the audit logging protocol, use one of:
CONFIG SET AUDIT.PROTOCOL "file /path/to/logfile"
CONFIG SET AUDIT.PROTOCOL syslog local0
CONFIG SET AUDIT.PROTOCOL tcp "127.0.0.1:9514"
To sets the audit log format:
CONFIG SET AUDIT.FORMAT text
CONFIG SET AUDIT.FORMAT json
CONFIG SET AUDIT.FORMAT csv
To set the which event categories to audit, use one of the below:
CONFIG SET AUDIT.EVENTS all # Enable all events
CONFIG SET AUDIT.EVENTS none # Disable all events
CONFIG SET AUDIT.EVENTS connections,auth # Enable only connection and auth events
Available event categories:
connections: Client connections and disconnectionsauth: Authentication attempts (with password redaction)config: Configuration commandskeys: Key operationsother: Operations that are not config and are not key operations
Controls which command executions produce an audit log entry.
CONFIG SET AUDIT.COMMAND_RESULT_MODE failures # default
CONFIG SET AUDIT.COMMAND_RESULT_MODE all
failures (default) — logs only commands that did not complete successfully:
- Commands that executed but returned an error (e.g.
WRONGTYPE, out-of-range index) - Commands rejected before execution: wrong argument count, unknown command, OOM, read-only replica, busy script, etc.
- ACL rejections:
NOPERM(command, key, channel, or database) andNOAUTH
Successful command executions produce no log entry. The server performs an O(1) listener-count check and skips all event preparation for successful commands, so there is zero per-command overhead for clean traffic.
all — additionally logs every successful command execution. Equivalent in coverage to the pre-execution command filter approach, with the added benefit of capturing actual execution outcome, duration, and keys modified.
Note: this setting takes effect at module load time. Changing it at runtime updates the stored value but the active event subscription does not change until the module is reloaded.
Configure options for payload logging:
CONFIG SET AUDIT.PAYLOAD_DISABLE yes|no
CONFIG SET AUDIT.PAYLOAD_MAXSIZE 1024
To retrieve the current complete audit configuration:
CONFIG GET AUDIT.*
To retrieve the current specific audit configuration parameter:
CONFIG GET AUDIT.FORMAT
Rules to set usernames and/or IP addresses to be excluded from auditing through a comma-separated list. The rule formats are :
username # for username-only exclusion
@ipaddress # for IPaddress-only exclusion
username@ipaddress # combination exclusion
Example
CONFIG SET AUDIT.EXCLUDERULES "un1,@192.168.1.12,un2@192.168.1.22"
To remove the current list of exclusion rules
CONFIG SET AUDIT.EXCLUDERULES ""
Setting the TCP connection parameters:
CONFIG SET audit.tcp_host 127.0.0.1
CONFIG SET audit.tcp_port 9514
CONFIG SET audit.tcp_timeout_ms 1000
CONFIG SET audit.tcp_retry_interval_ms 3000
CONFIG SET audit.tcp_max_retries 3
Behaviour for disconnects:
CONFIG SET audit.tcp_buffer_on_disconnect yes
CONFIG SET audit.tcp_reconnect_on_failure yes
Additional options to exclude are available through specific command exclusions, command prefix exclusion (e.g. for modules) and creation of a custom category
CONFIG SET audit.exclude_commands "PING,ECHO,TIME"
These need to be preceeded with a !
CONFIG SET audit.prefix_filter "!FT*"
Custom categories can be created by the user and subsequently added to the events exclusion list
The format here is "categoryname:commands in this category"
CONFIG SET audit.custom_category "flushes:FLUSHDB,FLUSHALL"
CONFIG SET audit.custom_category "admin:CONFIG,ACL,SAVE,BGSAVE"
CONFIG SET audit.events "connections,auth,flushes,admin"
The project has a collection of scripts to start a Valkey server using docker-compose to easily test the module.
To start a Valkey CLI shell to test the module commands, run:
./scripts/run_test_cli.shThe above command will start the Valkey server, and opens the valkey CLI shell. When the shell closes, it also stops the Valkey server.
If you just want to start the Valkey server, run:
./scripts/start_valkey.shYou can connect to the Valkey server from the localhost address.
To stop the servers, run:
./scripts/stop_valkey.shThe unit tests are written in python and can be found in the test/unit directory. They will start a local valkey server with the module loaded.
Requirements:
- valkey installation, environment variable VALKEY_SERVER if not in the path
- environment variable AUDIT_MODULE_PATH to point at the module shared library libvalkeyaudit.so
To do: automation
Every audit log entry contains the following fields regardless of format:
| Field | Description |
|---|---|
timestamp |
Local time in YYYY-MM-DD HH:MM:SS |
category |
Event category: CONNECTION, AUTH, CONFIG, KEY_OP, OTHER |
command |
Command name as reported by the server (e.g. set, config|get) |
command_args |
Command-specific arguments (see below) |
result |
Outcome: SUCCESS, FAILURE, ATTEMPT |
duration_us |
Execution time in microseconds |
keys_modified |
Number of keys modified (0 for reads and failures) |
client_id |
Numeric Valkey client ID |
username |
Authenticated username |
client_ip |
Client IP address |
client_port |
Client TCP port |
server_hostname |
Hostname of the Valkey server |
error |
Populated only for rejected events: rejected=<msg> or acl_deny_reason=<reason> acl_object=<name> |
| Category | Contents |
|---|---|
KEY_OP |
key=<key> [payload=<value>] |
CONFIG |
subcommand=<GET|SET> [param=<name>] |
AUTH |
password=<REDACTED> |
OTHER |
arg1=<val> [arg2=<val>] [arg3=<val>] |
CONNECTION |
type=<connection-type> |
[2026-01-21 09:54:07] [KEY_OP] set result=SUCCESS duration_us=6 keys_modified=1 client_id=4 username=default client_ip=127.0.0.1:30414 server_hostname=myserver key=user:1001 payload=hello
[2026-01-21 09:54:08] [KEY_OP] lpush result=FAILURE duration_us=12 keys_modified=0 client_id=4 username=default client_ip=127.0.0.1:30414 server_hostname=myserver key=mystring
[2026-01-21 09:54:09] [AUTH] set result=FAILURE duration_us=3 keys_modified=0 client_id=5 username=keyuser client_ip=127.0.0.1:30415 server_hostname=myserver key=forbidden:key acl_deny_reason=key acl_object=forbidden:key
[2026-01-21 09:54:10] [CONNECTION] connection result=SUCCESS duration_us=0 keys_modified=0 client_id=16 username=default client_ip=127.0.0.1:30416 server_hostname=myserver type=normal
[2026-01-21 09:54:11] [AUTH] AUTH result=ATTEMPT duration_us=0 keys_modified=0 client_id=16 username=alice client_ip=127.0.0.1:30416 server_hostname=myserver password=<REDACTED>
[2026-01-21 09:54:12] [CONFIG] config|get result=SUCCESS duration_us=45 keys_modified=0 client_id=4 username=default client_ip=127.0.0.1:30414 server_hostname=myserver subcommand=GET param=maxmemory
Each entry is a single-line JSON object:
{"timestamp":"2026-01-21 09:54:07","category":"KEY_OP","command":"set","command_args":"key=user:1001 payload=hello","result":"SUCCESS","duration_us":6,"keys_modified":1,"client_id":4,"username":"default","client_ip":"127.0.0.1","client_port":30414,"server_hostname":"myserver","error":""}
{"timestamp":"2026-01-21 09:54:08","category":"KEY_OP","command":"lpush","command_args":"key=mystring","result":"FAILURE","duration_us":12,"keys_modified":0,"client_id":4,"username":"default","client_ip":"127.0.0.1","client_port":30414,"server_hostname":"myserver","error":""}
{"timestamp":"2026-01-21 09:54:09","category":"AUTH","command":"set","command_args":"key=forbidden:key","result":"FAILURE","duration_us":3,"keys_modified":0,"client_id":5,"username":"keyuser","client_ip":"127.0.0.1","client_port":30415,"server_hostname":"myserver","error":"acl_deny_reason=key acl_object=forbidden:key"}Columns (13 total): timestamp, category, command, command_args, result, duration_us, keys_modified, client_id, username, client_ip, client_port, server_hostname, error. Commas within a field are escaped with a backslash.
2026-01-21 09:54:07,KEY_OP,set,key=user:1001 payload=hello,SUCCESS,6,1,4,default,127.0.0.1,30414,myserver,
2026-01-21 09:54:08,KEY_OP,lpush,key=mystring,FAILURE,12,0,4,default,127.0.0.1,30414,myserver,
2026-01-21 09:54:09,AUTH,set,key=forbidden:key,FAILURE,3,0,5,keyuser,127.0.0.1,30415,myserver,acl_deny_reason=key acl_object=forbidden:key
This module is licensed under the same terms as Valkey itself.