Valkey 9
Overview
Valkey is the Linux Foundation fork of Redis 7.2, created after the Redis trademark and license change in 2024. It speaks the Redis wire protocol, so existing Redis client libraries connect without code changes. These include redis-py for Python, ioredis and node-redis for Node, StackExchange.Redis for .NET, Jedis and Lettuce for Java, and go-redis for Go. Existing Redis command and script libraries also work unchanged.
This SecureAnyCloud image runs Valkey 9 as a single-node, in-memory key-value store on Ubuntu 22.04 LTS. It is intended for teams that want a ready-to-use single-node cache or data store from day one, without spending time on packaging, systemd configuration, persistence settings or password management.
The image is not a replicated Valkey cluster, it is not TLS-encrypted out of the box, and it does not ship with Sentinel, Valkey Cluster sharding or the RedisInsight companion UI. Step 14 describes how to add TLS for production traffic.
What you get:
- Valkey 9 running as a single node on Ubuntu 22.04 LTS, managed by systemd
- Authentication on the network listener through the classic requirepass directive
- A unique admin password generated on the first boot of every deployed virtual machine, so two virtual machines launched from the same gallery image never share credentials
- Append-only file persistence enabled by default, with RDB snapshots as a secondary durability layer
- The default eviction policy allkeys-lru, with maxmemory sized to approximately 75 percent of the virtual machine's RAM at install time
- 24/7 SecureAnyCloud support
Prerequisites
Before you deploy this image, make sure you have:
- A Microsoft Azure subscription in which you can create resource groups, virtual networks and virtual machines
- Azure role permissions equivalent to Contributor on the target resource group
- An SSH public key for the first login to the admin user account
- A virtual network and subnet, with an associated network security group, in the same region as the Azure Compute Gallery that the image is published into
- The Azure CLI (
azversion 2.50 or later) installed locally, if you plan to deploy from the command line as described in Step 2 - The SecureAnyCloud Valkey 9 offer enabled on your tenant in Azure Marketplace
Deploy the virtual machine from the Azure Portal
In the Azure Portal, go to Marketplace, search for Valkey 9, and select the SecureAnyCloud publisher entry. Select Create to start the wizard.
On the Basics tab, choose your subscription, target resource group and region. The region must match the region in which your Azure Compute Gallery exposes the image. Set the virtual machine name. Choose SSH public key as the authentication type, enter a username of your choice, and paste your SSH public key.
Standard_B2s is a reasonable starting size. Valkey is lightweight at idle, using around 5 megabytes of resident memory before you load data. The limiting factor is how much data you plan to hold in memory, so size the virtual machine's RAM to cover your working set plus headroom for the append-only file rewrite buffer.
On the Disks tab, choose Standard SSD as the OS disk type and leave the OS disk size at the default. If you plan to move the Valkey data directory to a separate Premium SSD data disk, you can attach it now, or attach it after the server is running by following Step 13.
On the Networking tab, select your existing virtual network and subnet. Attach a network security group that allows inbound TCP 22 from your management IP range. Allow inbound TCP 6379 only from the virtual network CIDR or the specific application server subnets that need to reach the server.
Do not expose 6379 to the public internet. Protected mode plus a password provides authentication only, with no transport encryption unless you configure TLS as described in Step 14. Unauthenticated Valkey and Redis instances on the public internet are among the most heavily scanned and exploited attack surfaces in the cloud.
The defaults on the Management, Monitoring and Advanced tabs are appropriate. Select Review + create, wait for validation to pass, then select Create. Deployment takes around two minutes.
Deploy the virtual machine from the Azure CLI
To deploy from the command line, accept the Marketplace terms once, then create the VM from the Marketplace image URN:
RG="valkey-prod" LOCATION="eastus" VM_NAME="valkey-01" ADMIN_USER="valkeyops" IMAGE_URN="sac1647283583153:valkey-9-ubuntu-22-04:valkey-9-ubuntu-22-04-gen2:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION" az network vnet create \
--resource-group "$RG" \
--name valkey-vnet \
--address-prefix 10.30.0.0/16 \
--subnet-name valkey-subnet \
--subnet-prefix 10.30.1.0/24 az network nsg create --resource-group "$RG" --name valkey-nsg az network nsg rule create \
--resource-group "$RG" --nsg-name valkey-nsg \
--name allow-ssh-mgmt --priority 100 \
--source-address-prefixes "<your-mgmt-cidr>" \
--destination-port-ranges 22 --access Allow --protocol Tcp az network nsg rule create \
--resource-group "$RG" --nsg-name valkey-nsg \
--name allow-valkey-vnet --priority 110 \
--source-address-prefixes 10.30.0.0/16 \
--destination-port-ranges 6379 --access Allow --protocol Tcp
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:valkey-9-ubuntu-22-04:valkey-9-ubuntu-22-04-gen2:latest
az vm create \
--resource-group "$RG" \
--name "$VM_NAME" \
--image "$IMAGE_URN" \
--size Standard_B2s \
--storage-sku StandardSSD_LRS \
--admin-username "$ADMIN_USER" \
--ssh-key-values "$SSH_KEY" \
--vnet-name valkey-vnet --subnet valkey-subnet \
--nsg valkey-nsg \
--public-ip-address "" \
--os-disk-size-gb 32The --public-ip-address "" flag keeps the server off the public internet. Reach it through a bastion host or your existing private connectivity.
Connect over SSH
After deployment, find the private IP address of the new virtual machine. Then connect from a host inside the same virtual network:
ssh valkeyops@<private-ip>The first login can take a few seconds while cloud-init finishes. When you reach a shell, systemd has already started the server, and the first-boot one-shot service has already generated the per-VM password.
Retrieve the Valkey credentials
The valkey-firstboot.service systemd one-shot writes the admin password the first time the virtual machine boots. The password is stored in a single file that only root can read:
sudo cat /stage/scripts/valkey-credentials.logThe output looks similar to this:
username=default password=4f8aB2dQ9pK1xVnE7sT3uYrL6cZw0jHm port=6379 sample_connect=valkey-cli -a "4f8aB2dQ9pK1xVnE7sT3uYrL6cZw0jHm" PINGThese credentials are unique to this virtual machine. Store them somewhere safe, then rotate them as described in Step 11. The first-boot one-shot does not run again on later reboots, so this file is your only readable copy of the generated password until you rotate it.
Server components
The deployed image contains the following components:
- Valkey 9.0.3: single-node, in-memory key-value store
- Ubuntu 22.04 LTS: base operating system
- systemd units valkey-server.service and valkey-firstboot.service: process supervision and first-boot password generation
The Valkey server process is /usr/bin/valkey-server /etc/valkey/valkey.conf. It runs under the dedicated valkey system user, which has no login shell, and is started by the SecureAnyCloud-authored valkey-server.service unit with Type=notify for tight systemd integration.
Valkey is compiled from the official upstream 9.0.3 source release with USE_SYSTEMD=yes and BUILD_TLS=yes. This means you can enable native TLS on port 6380 as described in Step 14 without recompiling. The source tree lives at /usr/src/valkey-9.0.3, so you can rebuild with different flags in place.
On the first boot, the companion valkey-firstboot.service one-shot runs before valkey-server.service. It replaces the default requirepass value in valkey.conf with a per-VM unique password.
Filesystem layout
The image uses the following paths, listed with owner and mode where applicable:
- /usr/bin/valkey-server (root:root): Valkey server binary, compiled from upstream source and installed with make install PREFIX=/usr
- /usr/bin/valkey-cli (root:root): command-line client
- /usr/src/valkey-9.0.3/ (root:root): upstream source tree, retained for in-place rebuilds
- /etc/valkey/valkey.conf (valkey:valkey, 0640): server configuration, including requirepass
- /etc/systemd/system/valkey-server.service (root:root, 0644): SecureAnyCloud-authored systemd unit with Type=notify and first-boot ordering
- /etc/systemd/system/valkey-firstboot.service (root:root, 0644): first-boot one-shot unit
- /usr/local/sbin/valkey-firstboot.sh (root:root, 0750): first-boot logic for password generation and config substitution
- /usr/local/sbin/valkey-start.sh (root:root, 0755): helper that wraps systemctl start
- /usr/local/sbin/valkey-stop.sh (root:root, 0755): helper that wraps systemctl stop
- /var/lib/valkey/ (valkey:valkey, 0750): data directory containing dump.rdb and appendonlydir/
- /var/log/valkey/ (valkey:valkey, 0750): server logs
- /run/valkey/valkey-server.pid (valkey:valkey): PID file on tmpfs, regenerated at each start
- /stage/scripts/valkey-credentials.log (root:root, 0600): generated admin password, readable by root only
Start, stop and check status
systemd starts the server at boot. Manage it with the following commands:
# Status
sudo systemctl status valkey-server.service
# Stop
sudo systemctl stop valkey-server.service
# Start
sudo systemctl start valkey-server.service
# Restart
sudo systemctl restart valkey-server.service
# Tail live logs
sudo journalctl -u valkey-server.service -fCheck the first-boot one-shot with this command:
sudo systemctl status valkey-firstboot.service
sudo journalctl -u valkey-firstboot.serviceAfter a successful first boot, the one-shot is expected to remain in the active (exited) state. The file /var/lib/valkey/.firstboot-done gates it, so it does not run again.
Connect with valkey-cli
Export the generated password once per shell session, then run valkey-cli:
export VALKEY_PASSWORD="$(sudo awk -F= '/^password=/ {print $2}' /stage/scripts/valkey-credentials.log)" valkey-cli -a "$VALKEY_PASSWORD" PINGExpected output:
PONGInspect the server's self-reported identity:
valkey-cli -a "$VALKEY_PASSWORD" INFO server | head -20The output shows the Valkey server version (valkey_version:9.0.3) and the Redis wire protocol compatibility version (redis_version:7.2.4). Later lines show the process ID, the operating system and the uptime in seconds. The server_mode line reports standalone, which confirms a single-node deployment rather than a Valkey Cluster shard.
Run basic key-value operations
Valkey implements the full Redis command family. The following round trip exercises strings, counters and lists:
valkey-cli -a "$VALKEY_PASSWORD" SET mykey "Hello Valkey" valkey-cli -a "$VALKEY_PASSWORD" GET mykey valkey-cli -a "$VALKEY_PASSWORD" INCR counter valkey-cli -a "$VALKEY_PASSWORD" INCR counter valkey-cli -a "$VALKEY_PASSWORD" INCR counter valkey-cli -a "$VALKEY_PASSWORD" LPUSH mylist "item1" "item2" "item3" valkey-cli -a "$VALKEY_PASSWORD" LRANGE mylist 0 -1 valkey-cli -a "$VALKEY_PASSWORD" DEL mykey counter mylistExpected output:
OK "Hello Valkey" (integer) 1 (integer) 2 (integer) 3 (integer) 3 1) "item3" 2) "item2" 3) "item1" (integer) 3Confirm that append-only file persistence is active and that the configured maxmemory policy matches your expectation:
valkey-cli -a "$VALKEY_PASSWORD" CONFIG GET appendonly valkey-cli -a "$VALKEY_PASSWORD" CONFIG GET maxmemory-policy valkey-cli -a "$VALKEY_PASSWORD" CONFIG GET maxmemoryExpected output (the maxmemory byte value varies with the virtual machine's RAM):
1) "appendonly" 2) "yes" 1) "maxmemory-policy" 2) "allkeys-lru" 1) "maxmemory" 2) "<bytes>"Connect from application servers
Because Valkey speaks the Redis wire protocol, existing Redis client libraries connect without code changes. The examples assume that the virtual machine's private IP is 10.30.1.4 and that you have loaded the generated password into the environment variable VALKEY_PASSWORD on your application server.
Python, using redis:
import os, redis r = redis.Redis( host="10.30.1.4", port=6379, password=os.environ["VALKEY_PASSWORD"], decode_responses=True, ) r.set("greeting", "hello from redis-py") print(r.get("greeting"))Node.js, using ioredis:
import Redis from "ioredis"; const client = new Redis({ host: "10.30.1.4", port: 6379, password: process.env.VALKEY_PASSWORD, }); await client.set("greeting", "hello from ioredis"); console.log(await client.get("greeting"));Java, using Jedis:
import redis.clients.jedis.JedisPooled; JedisPooled client = new JedisPooled( "10.30.1.4", 6379, null, System.getenv("VALKEY_PASSWORD") ); client.set("greeting", "hello from jedis"); System.out.println(client.get("greeting"));To test connectivity quickly, run this from a shell on your application server:
valkey-cli -h 10.30.1.4 -p 6379 -a "$VALKEY_PASSWORD" PINGExpected output:
PONGIf the application server does not have the Valkey APT package, the redis-cli binary from the Redis distribution works identically, as does any other Redis client.
Rotate the admin password
The generated requirepass lives in /etc/valkey/valkey.conf. Rotate it as follows:
NEW_PW="$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 32)"
sudo valkey-cli -a "$VALKEY_PASSWORD" CONFIG SET requirepass "$NEW_PW"
sudo sed -i "s|^requirepass .*|requirepass ${NEW_PW}|" /etc/valkey/valkey.conf
sudo install -o root -g root -m 0600 /dev/null /stage/scripts/valkey-credentials.log
sudo tee /stage/scripts/valkey-credentials.log >/dev/null <<EOF username=default password=${NEW_PW} port=6379 sample_connect=valkey-cli -a "${NEW_PW}" PING EOF export VALKEY_PASSWORD="$NEW_PW" valkey-cli -a "$VALKEY_PASSWORD" PINGThe CONFIG SET requirepass call changes the running password without restarting the server, so in-flight client connections are not dropped. The sed call updates the on-disk configuration so that the new password survives a service restart. Confirm the change with a fresh PING, then distribute the new password to every application that connects to this server.
Create named ACL users for application access
The default user carries the master password and has full command access. For each application that connects to this server, create a named ACL user limited to the commands and keyspaces that application needs:
APP_USER="orders_api" APP_PW="$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 32)" valkey-cli -a "$VALKEY_PASSWORD" ACL SETUSER "$APP_USER" on ">$APP_PW" ~orders:* +@read +@write +@string +@hash +@list -@admin -@dangerous valkey-cli -a "$VALKEY_PASSWORD" ACL LIST
sudo cp /etc/valkey/valkey.conf /etc/valkey/valkey.conf.bak
sudo tee -a /etc/valkey/valkey.conf >/dev/null <<EOF
# ACL users added by sac Section 12 user ${APP_USER} on >${APP_PW} ~orders:* +@read +@write +@string +@hash +@list -@admin -@dangerous EOFTest the new user with these commands:
valkey-cli -h 127.0.0.1 -p 6379 --user "$APP_USER" -a "$APP_PW" SET orders:42 '{"status":"pending"}' valkey-cli -h 127.0.0.1 -p 6379 --user "$APP_USER" -a "$APP_PW" GET orders:42Expected output:
OK "{\"status\":\"pending\"}"Valkey rejects attempts to read or write keys outside the orders: prefix, or to run administrative commands, with a NOPERM error. This is the recommended pattern for production: give every application its own ACL user, its own password, and the minimum command and keyspace scope it needs.
Move data to an attached Premium SSD disk
If you attached a Premium SSD data disk at deploy time, format and mount it, then move /var/lib/valkey onto it:
# List disks to find the new device, typically /dev/sdc on Azure lsblk
# Format and mount
sudo mkfs.ext4 -L valkey-data /dev/sdc
sudo mkdir -p /mnt/valkey-data
sudo mount /dev/sdc /mnt/valkey-data echo "LABEL=valkey-data /mnt/valkey-data ext4 defaults,nofail 0 2" | sudo tee -a /etc/fstab
# Stop the server, copy the data, replace the directory with a bind mount
sudo systemctl stop valkey-server.service
sudo rsync -aHAX --numeric-ids /var/lib/valkey/ /mnt/valkey-data/
sudo mv /var/lib/valkey /var/lib/valkey.prev
sudo mkdir -p /var/lib/valkey
sudo chown valkey:valkey /var/lib/valkey
sudo mount --bind /mnt/valkey-data /var/lib/valkey echo "/mnt/valkey-data /var/lib/valkey none bind 0 0" | sudo tee -a /etc/fstab
# Restart
sudo systemctl start valkey-server.service valkey-cli -a "$VALKEY_PASSWORD" PING
sudo rm -rf /var/lib/valkey.prevThe append-only file and RDB snapshots now live on the Premium SSD, and the server still finds them at /var/lib/valkey because of the bind mount. This keeps the Valkey configuration path stable while letting you scale storage independently of the OS disk.
Enable TLS for production traffic
Valkey supports native TLS on a separate port, conventionally 6380. For production traffic outside a trusted private network, terminate TLS on Valkey itself rather than adding a proxy. The outline below shows the steps:
# Place your server certificate, private key, and the CA bundle in /etc/valkey/tls/
sudo mkdir -p /etc/valkey/tls
sudo chown valkey:valkey /etc/valkey/tls
sudo chmod 0750 /etc/valkey/tls
# Copy valkey.crt, valkey.key, ca.crt into /etc/valkey/tls/ with 0640 valkey:valkey
sudo tee -a /etc/valkey/valkey.conf >/dev/null <<'EOF'
# TLS configuration added by sac Section 14 tls-port 6380 tls-cert-file /etc/valkey/tls/valkey.crt tls-key-file /etc/valkey/tls/valkey.key tls-ca-cert-file /etc/valkey/tls/ca.crt tls-auth-clients no EOF
sudo systemctl restart valkey-server.service valkey-cli -a "$VALKEY_PASSWORD" --tls \
--cert /etc/valkey/tls/valkey.crt \
--key /etc/valkey/tls/valkey.key \
--cacert /etc/valkey/tls/ca.crt \
-h <hostname-matching-cert> -p 6380 PINGWhen you have verified that TLS works, open NSG rule 6380 to your application subnets. Then either disable port 6379 entirely by setting port 0 in valkey.conf, or restrict it with tls-auth-clients yes and client certificate verification. Once TLS is enabled, do not leave 6379 open on any interface that external traffic can reach.
Troubleshoot common problems
Cannot connect remotely. Verify that the server is bound to 0.0.0.0 rather than 127.0.0.1 by running sudo ss -tlnp | grep :6379. Verify that your network security group allows the source subnet on TCP 6379. Verify that the client uses the correct password by running the same command locally with valkey-cli -a "$VALKEY_PASSWORD" PING. A common misconfiguration is leaving protected-mode yes while also leaving the password blank. The SecureAnyCloud image ships with both a bind of 0.0.0.0 and a non-blank password, so this combination works out of the box.
High memory pressure or OOM kills. Check valkey-cli -a "$VALKEY_PASSWORD" INFO memory. The used_memory_human line tells you current footprint, the maxmemory_human line tells you the configured ceiling, and the maxmemory_policy line tells you how Valkey evicts when it hits the ceiling. The default allkeys-lru policy evicts the least recently used keys across all databases. If you need different behaviour, set a different policy with CONFIG SET maxmemory-policy <policy> and persist it to /etc/valkey/valkey.conf. Consult the Valkey documentation for the full policy list.
Append only file grows unbounded. The AOF rewrites automatically when it doubles in size past the auto-aof-rewrite-min-size threshold of 64 megabytes. If disk pressure is building, check valkey-cli -a "$VALKEY_PASSWORD" INFO persistence. You can force a rewrite with valkey-cli -a "$VALKEY_PASSWORD" BGREWRITEAOF. If AOF rewrites repeatedly fail, check /var/log/valkey/valkey-server.log for the underlying reason, typically disk full or permission issues on /var/lib/valkey/appendonlydir/.
Server refuses connections after reboot. Confirm the first boot oneshot completed by running sudo systemctl status valkey-firstboot.service and checking that the status is active (exited) and the sentinel /var/lib/valkey/.firstboot-done exists. If the oneshot failed, check sudo journalctl -u valkey-firstboot.service for the reason, fix the cause, sudo rm /var/lib/valkey/.firstboot-done, then sudo systemctl start valkey-firstboot.service followed by sudo systemctl start valkey-server.service.
Security recommendations
- Keep Valkey off the public internet. Never expose TCP 6379 or 6380 publicly. Unauthenticated or weakly authenticated Valkey and Redis instances are a top target for opportunistic scanners, and even a strong password does not replace network segmentation.
- Rotate the generated admin password on first login. Follow Step 11. The file
/stage/scripts/valkey-credentials.logis your only readable copy of the generated password, so read it, rotate the password, and store the new value in your secrets manager. - Use named ACL users. Create one per application, as described in Step 12, instead of sharing the
defaultuser's credentials. Grant only the commands and keyspaces each application needs. - Enable TLS. Follow Step 14 before you expose Valkey to anything other than a tightly scoped private network. Prefer Valkey native TLS on 6380 over a sidecar proxy for the lowest-latency path.
- Restrict dangerous commands for application ACL users.
-@dangerousblocksKEYS,FLUSHALL,FLUSHDB,DEBUG,SHUTDOWN,CONFIGand related commands. Thedefaultuser retains access to these for operator use. - Monitor the VM. Enable Azure Monitor alerting on the virtual machine's memory pressure and on the
/var/lib/valkeydisk fill percentage. An AOF that cannot rewrite because the disk is full is a silent outage waiting to happen.
Support and licensing
The Linux Foundation distributes Valkey under the three-clause BSD license, and this image bundles it unmodified from the official packages.valkey.io APT repository. Canonical distributes Ubuntu 22.04 LTS under the terms shown by dpkg -L base-files on the running server.
The SecureAnyCloud image itself is distributed under the Microsoft Azure Marketplace standard contract terms, with pay-as-you-go pricing shown on the Partner Center plan. SecureAnyCloud provides best-effort, image-level support. Send questions about the image, the first-boot mechanism, the systemd drop-ins, the ACL user pattern or the TLS configuration in Step 14 to https://secureanycloud.com/support. Report upstream Valkey bugs at https://github.com/valkey-io/valkey/issues.
Deploy on Azure
Launch Valkey 9 on Ubuntu 22.04 on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com