An unauthenticated Valkey reachable from the internet is not a data leak, it is a remote shell. The command set lets a client change where the server writes its snapshot file and what that file is called, which is enough to drop an SSH key or a cron file onto the machine as the user Valkey runs as. Automated scanners find open instances within minutes, and what they leave behind is usually a cryptocurrency miner. Every hardening step below exists because of that one fact.
The defence, in order of importance: never expose it without authentication; use a long random password; give each application its own ACL user with only the keys and commands it needs; keep administrative and destructive commands away from application credentials; and know that the protocol is plain text unless TLS is configured. This post goes through each, with the actual configuration and commands.
Why exposed instances get compromised#
The attack is old and simple, and knowing its shape explains the defences.
- Scan the internet for port
6379(and other common ports) answering the Redis protocol. - Send
INFO. If it answers withoutNOAUTH, the instance is open. - Use
CONFIG SET dirandCONFIG SET dbfilenameto point snapshot output at a sensitive path -/root/.ssh/andauthorized_keys, or a cron directory. - Write a key whose value contains an SSH public key or a cron line, run
SAVE, and the snapshot file now contains a usable payload. - Log in, or wait for cron, and install a miner.
Variants load a malicious module with MODULE LOAD, or abuse replication with REPLICAOF to pull a module from an attacker's server. All of them need two things: a connection without authentication and permission to run administrative commands. Take away either and the attack fails.
Modern Valkey already closes part of this by default. enable-protected-configs, enable-debug-command and enable-module-command all default to no, which blocks changing sensitive paths such as dir at runtime and blocks MODULE LOAD from clients. That is a real improvement over servers from a few years ago, and it is still not a reason to leave an instance open: the data itself is readable, writable and deletable by anyone who connects.
Protected mode and binding#
Two settings decide who can even connect.
bind 127.0.0.1 -::1protected-mode yesport 6379bind lists the interfaces Valkey listens on. On a machine where only local processes need it, loopback is the right answer and removes most of the risk outright. The leading - on -::1 means "do not fail to start if this address is unavailable".
protected-mode yes is a safety net: if no password is set and bind has not been changed from its default, the server refuses connections from anything but loopback and tells the client why. It does not protect an instance that has been explicitly bound to a public address with no password - that is a choice the server assumes you meant.
When the application runs on a different machine, which is the normal case for a hosted Valkey, the server must listen on a reachable address, and the password becomes the main defence. Restricting which source addresses may connect, at a firewall in front of the port, adds a second one where you control that firewall.
On RE:NODE a Valkey plan is created with a password generated for that server and is reached on the plan's own host and port; database plans have no proxy slot in front. The instance is never sold in an unauthenticated state, which removes the classic failure. What remains your job is keeping the password out of places it should not be, and the rest of this post.
Passwords: requirepass and AUTH#
The simplest authentication is a single shared password:
requirepass 9fK2pQ7x-a-long-random-string-from-a-generatorClients then authenticate with AUTH <password> before any other command, or put it in the connection URL: redis://:password@host:port/0. Under the hood, requirepass sets the password of the built-in default ACL user, so AUTH default <password> works too.
What makes a password adequate here is different from a login form. Valkey is fast enough that an attacker who can connect can try a very large number of passwords per second, and there is no lockout. Use 32 or more random characters from a generator, not a memorable phrase. Generate one with:
$ openssl rand -base64 32If it will go into a URL, avoid or percent-encode @, /, : and #, which break URL parsing in many clients - or use openssl rand -hex 32, which produces only safe characters.
Keep it where your other secrets are: environment variables or a secrets store, never committed to the repository and never pasted into a support chat or an issue tracker. Environment variables and secrets covers where they should live, and the database security checklist applies to Valkey as much as to any SQL server.
Rotating a single shared password means changing it on the server and in every client at once, which is awkward. ACL users solve that too.
Where passwords leak from in practice
The password is rarely stolen from Valkey. It leaks from around it:
- Logs. A connection URL printed at startup ("connecting to redis://:hunter2@...") ends up in log files, log aggregators and screenshots. Log the host and port, never the URL.
- Error reports. Exception trackers capture environment variables and configuration objects by default. Scrub the variable that holds the URL.
- Client-side bundles. A front-end build that reads environment variables can embed one that was only meant for the server. Valkey credentials must never reach a browser.
- Shared screens and tickets. A pasted
.envfile is a published password. - Former team members. One shared password known to everybody who ever worked on the project is a password you cannot revoke from one person.
That last one is the strongest argument for per-application ACL users and for rotation as a routine rather than an emergency. When someone leaves, rotate.
ACL users: one per application#
Access control lists, inherited from the Redis 6 design, let you create named users, each with its own passwords, a set of allowed commands and a set of allowed key patterns. The point is least privilege: a compromised web application should not be able to FLUSHALL, read another application's keys or reconfigure the server.
ACL SETUSER shop on >s3cr3t-shop-password ~shop:* &shop:* +@all -@dangerousACL SETUSER reports on >s3cr3t-report-password ~shop:* resetchannels -@all +@read +pingACL SETUSER default offReading the first line from left to right:
| Rule | Meaning |
|---|---|
on | The user is enabled |
>password | Adds a password (a user can have several) |
~shop:* | May access keys matching shop:* only |
&shop:* | May use pub/sub channels matching shop:* |
+@all | Allows every command category... |
-@dangerous | ...then removes the dangerous category |
Rules apply in order, so +@all -@dangerous is "everything except dangerous commands". The second user is read-only and has no channels at all. The third line disables the default user, which means nobody can connect without naming a user - only do that after confirming every client authenticates with a username.
Clients authenticate with AUTH shop <password>, or with the username in the URL: redis://shop:password@host:port/0. Most current client libraries support it; very old ones that only send AUTH <password> can only use the default user.
Useful inspection commands:
ACL WHOAMIACL LISTACL GETUSER shopACL CAT dangerousACL DRYRUN shop FLUSHALLACL LOG 10ACL DRYRUN tests whether a user would be allowed a command without running it. ACL LOG records recent denials and failed authentications, which is both a debugging tool and an early warning that someone is guessing passwords.
Users created with ACL SETUSER live in memory. To survive a restart they must be saved: either to an ACL file configured with aclfile and written with ACL SAVE, or written into valkey.conf as user lines and persisted with CONFIG REWRITE. Forgetting this step means the careful permissions vanish on the next restart.
Whether you can manage ACL users yourself on a hosted instance depends on the permissions of the user you were given. Run ACL WHOAMI and ACL LIST; if the latter is refused, your user cannot administer ACLs, and the generated password is the access boundary - which is why it must be treated like a root password.
Dangerous commands#
The @dangerous category is the set of commands an application has no business running. A few that matter most:
| Command | Risk |
|---|---|
FLUSHALL, FLUSHDB | Deletes everything, instantly |
CONFIG | Reads and changes server settings |
KEYS | Walks the whole keyspace in one blocking call |
DEBUG | Can crash or stall the server |
SHUTDOWN | Stops the server |
MODULE | Loads native code |
REPLICAOF, SLAVEOF | Turns the server into a replica of an attacker's |
SAVE | Blocking snapshot |
MONITOR | Streams every command from every client, passwords included |
CLIENT subcommands | Lists and kills other connections |
The old method for disabling commands was rename-command in the configuration file, renaming FLUSHALL to an empty string or a random name. It still works, but it is all-or-nothing for every user and breaks tools that expect the command to exist. ACLs are the better tool: the application user simply cannot run FLUSHALL, while an admin user still can.
KEYS deserves its own mention because it is dangerous by accident rather than by malice. A developer's debug line running KEYS * against a production instance with millions of keys freezes it for seconds. Use SCAN with a MATCH pattern, and remove KEYS from application users.
Encryption in transit#
The Redis protocol is plain text. Without TLS, the AUTH command, the password in it and every value you read and write cross the network readable by anyone on the path.
Valkey supports TLS when built with it, configured with tls-port, tls-cert-file, tls-key-file and tls-ca-cert-file, and clients connect with the rediss:// scheme (two s). Whether a given hosted instance offers TLS on its port is a property of that service, so check before assuming it does - if the plan only lists a host and a port, assume plain TCP. Where TLS is not available:
- Keep the application and Valkey in the same location so traffic does not cross the public internet more than necessary.
- Do not store data in Valkey that would be a serious incident if read in transit: card numbers, plaintext personal records, long-lived secrets.
- Rotate the password if you ever suspect the path between application and server.
Never put Valkey behind an HTTP reverse proxy hoping it will add encryption. The protocol is not HTTP; a web proxy cannot carry it, and a TCP-level TLS terminator in front only protects the hop to that terminator.
Monitoring, rotation and incident response#
Security is also noticing when something is wrong.
- Watch `ACL LOG` for repeated authentication failures. A burst from an unknown address is someone guessing.
- Watch `INFO clients` for
connected_clientsfar above what your application opens. Unexpected connections are worth investigating. - Watch `INFO keyspace` and memory. A keyspace that suddenly empties, or fills with keys you do not recognise named like
backup1,backup2with odd values, is the signature of the attack described at the top. - Rotate passwords with ACL's multiple-password support: add the new password to the user, roll it out to every client, then remove the old one with
<oldpassword. No downtime, no flag day.
If you find evidence of compromise, assume the data was read and possibly altered. Change every credential stored in or used with Valkey, rotate the Valkey password, check the machines that connect to it, and restore from a known-good copy if values may have been tampered with. What to do when your server is hacked is the general playbook.
A checklist#
- Authentication is on: a password of 32 or more random characters, or ACL users.
- The port is reachable only by what needs it: loopback on a single machine, a firewall allow-list where you control one.
- Each application has its own ACL user, limited to its key prefix and without
@dangerous, where you can create users. - The
defaultuser is disabled or holds a strong password. - ACL changes are saved to an ACL file or the configuration, so they survive a restart.
- Passwords live in environment variables or a secrets store, not in code.
- TLS is used where offered; where not, sensitive data stays out of Valkey.
ACL LOG, client count and memory are watched.- There is a plan for rotation that does not require downtime.
FAQ#
Is requirepass enough on its own?
For a single application with a long random password and a port not exposed more widely than it must be, it is a reasonable baseline. ACL users add least privilege and painless rotation, and they matter more as the number of applications sharing an instance grows.
Can I hide Valkey by changing its port?
It reduces noise from the laziest scanners and nothing else. Scanners probe every port, and the protocol is easy to recognise. Change the port if you like, but authentication is what protects you.
Why does my client say NOAUTH after I set a username?
The client is sending AUTH <password> without the username, so the server tries it against the default user. Put the username in the URL or the client's username option, and check that the library version supports ACL authentication.
Are ACL passwords stored in plain text?
No. Valkey stores SHA-256 hashes of ACL passwords, and ACL GETUSER shows the hashes, not the passwords. The password still crosses the network in plain text unless TLS is in use.
Should the cache and the session store use different users?
Yes, if they are different applications or have different trust levels. Separate users with separate key prefixes mean a bug or compromise in one cannot read or flush the other's data.




Comments
Completely anonymous: no account, no email, no cookie. We store the name you type, the text and the time - nothing else. Links are limited and markup is not rendered.