Valkey
A Conn3x circuit that installs Valkey, the open-source fork of Redis, and sets it up as a password-protected cache or key-value store. You choose where it listens, how much memory it gets, what it evicts when that runs out, and whether data survives a restart. The password goes in at run time and never lands in state.
It also sets vm.overcommit_memory = 1, which Valkey asks for in its log on every start until you do. Without it, the fork behind a background save can fail on a busy host, and with persistence on that means writes start failing too.
variable "valkey_password" { type = string ephemeral = true sensitive = true}
module "valkey" { source = "github.com/conn3xorg/circuit-valkey?ref=v0.1.0"
hosts = { cache = "192.168.1.40" } ssh_private_key_file = "~/.ssh/id_ed25519"
password = var.valkey_password bind = ["127.0.0.1", "192.168.1.40"] maxmemory = "512mb" maxmemory_policy = "allkeys-lru"}Then run conn3x init and conn3x apply with the password in the environment:
export TF_VAR_valkey_password='...'conn3x applyA second conn3x plan says No changes. Every plan needs the password too, since Conn3x checks the file on the host against it.
For hosts created in the same plan, pass host objects with the host key you generated, so Conn3x checks it without a known_hosts entry. Give each one’s distribution in os too: the circuit picks its package and unit by distribution, and Conn3x can’t ask a host that doesn’t exist yet. If os turns out not to match the host, Conn3x stops before configuring it. Set os as well when you put depends_on on this module: depends_on holds back reading the hosts until the apply, so without os the plan fails with Invalid for_each argument.
hosts = { for name, d in digitalocean_droplet.cache : name => { address = d.ipv4_address host_key = tls_private_key.host[name].public_key_openssh os = "ubuntu" }}To try it on a throwaway container first, see examples/local.
Where Valkey comes from
Section titled “Where Valkey comes from”Each distribution’s own package. All four ship one, so there’s no third-party repository to trust and security fixes arrive with the rest of your updates. The catch is that you get the distribution’s version, not upstream’s latest:
| Distribution | Package | Version when tested | Repository |
|---|---|---|---|
| Ubuntu 24.04 | valkey-server |
7.2.13 | universe, noble-updates |
| Debian 13 | valkey-server |
8.1.1 | main |
| Rocky Linux 10 | valkey |
8.0.11 | AppStream |
| Fedora 45 | valkey |
9.1.2 | fedora |
Upstream was at 9.1.3. Everything this circuit sets works the same from 7.2 on, so the spread doesn’t change what you configure. It matters if your app needs a newer command, such as per-field expiry on hashes, which arrived in 9.0 and so is only on Fedora for now.
Upstream publishes no packages, only binary tarballs built on Ubuntu. Using those everywhere would pin one version, but the circuit would have to supply the user, the systemd unit and the SELinux labelling the packages already get right, and you’d be the one watching for security releases. That’s why there’s no version input.
What it does
Section titled “What it does”On each host, in order:
- On the Red Hat family, when
portisn’t 6379, installspolicycoreutils-python-utilsand labels the portredis_port_twithsemanage, so SELinux lets Valkey bind it. Hosts with SELinux off skip the label. - Installs
valkey-serveron Debian and Ubuntu, orvalkeyon the Red Hat family. - Sets
vm.overcommit_memory = 1, now and at boot, through/etc/sysctl.d/90-conn3x-vm.overcommit_memory.conf. - Writes
/etc/valkey/conn3x.confwithbind,port,maxmemory,maxmemory-policy,dirand the persistence settings, and/etc/valkey/conn3x-auth.confwithrequirepass. Both are mode 0640, owned the way the package ownsvalkey.conf. - Adds
include /etc/valkey/conn3x.confas the last line of/etc/valkey/valkey.conf. Valkey reads an include where it appears and a later setting wins, so yours override the distribution’s and the rest of its file still applies. - Restarts Valkey when any of those three files changed, then keeps it running and enabled at boot. Valkey has no reload, and
bind,portand the password only change on a restart.
If someone edits conn3x.conf by hand, the next plan shows the difference and the apply puts yours back and restarts Valkey.
Persistence
Section titled “Persistence”With rdb on, the default, Valkey saves a snapshot to /var/lib/valkey/dump.rdb on its usual schedule (after an hour with one change, five minutes with 100, or a minute with 10,000) and when it shuts down cleanly, then loads it on start. A restart or reboot keeps your data; a crash loses what changed since the last snapshot.
aof adds the append-only file, fsynced once a second, so a crash loses at most about a second of writes. Turn both off for a pure cache that starts empty.
Distribution differences
Section titled “Distribution differences”| Debian, Ubuntu | Rocky Linux, AlmaLinux, RHEL, Fedora | |
|---|---|---|
| systemd unit | valkey-server |
valkey |
Owner of the files in /etc/valkey |
valkey:valkey |
valkey:root |
The services output gives the unit per host. On the Red Hat family a tmpfiles rule in the package resets /etc/valkey to valkey:root at boot, which is why the circuit uses that owner there; anything else showed up as drift after a reboot.
What it doesn’t cover
Section titled “What it doesn’t cover”- ACL users. There’s one password, for the
defaultuser. Add named users withACL SETUSERor an ACL file yourself. - TLS, replication, Sentinel and cluster mode. None of these are set up.
- Firewall. The circuit doesn’t open the port. The firewall circuit can. Valkey speaks plain text, so keep it off the internet even with a password.
- Removal. Deleting the module leaves Valkey installed and running with the last settings.
Inputs
Section titled “Inputs”| Name | Type | Default | Description |
|---|---|---|---|
hosts |
any | Required | Hosts to configure, by name. Each value is an address, such as "203.0.113.10", or a host object such as { address = "203.0.113.10", host_key = "ssh-ed25519 AAAA..." }. A host object can also set user, port, private_key_file, jump_hosts and os, the distribution’s ID, such as ubuntu or rocky, which a host created in the same plan needs. The two forms can be mixed. |
ssh_user |
string | "root" |
User to log in as. |
ssh_port |
number | 22 |
SSH port. |
ssh_private_key_file |
string | null |
Path to a private key. When null, Conn3x uses the SSH agent. |
ssh_known_hosts_file |
string | null |
known_hosts file to check host keys against. When null, ~/.ssh/known_hosts. Not used for a host object with host_key. |
ssh_become |
bool | false |
Run commands as root through sudo. Set it when ssh_user isn’t root. |
password |
string, ephemeral, sensitive | Required | Password every client gives with AUTH, set as requirepass. Any characters but line breaks. |
bind |
list(string) | ["127.0.0.1", "-::1"] |
Addresses to listen on, such as ["0.0.0.0"]. A leading - lets Valkey start when that address is missing, which the default uses for IPv6 loopback. |
port |
number | 6379 |
TCP port. |
maxmemory |
string | "0" |
Memory for data before maxmemory_policy applies, such as "512mb" or "2gb". "0" means no limit. |
maxmemory_policy |
string | "noeviction" |
What happens at maxmemory. noeviction refuses writes; allkeys-lru, allkeys-lfu, allkeys-random, volatile-lru, volatile-lfu, volatile-random and volatile-ttl evict keys, which is what a cache wants. |
rdb |
bool | true |
Save RDB snapshots, so data survives a restart. |
aof |
bool | false |
Keep the append-only file as well, fsynced every second. |
Outputs
Section titled “Outputs”| Name | Description |
|---|---|
config_file |
Path of the settings file, /etc/valkey/conn3x.conf. |
services |
systemd unit running Valkey on each host, such as { cache = "valkey-server" }. |
Requirements
Section titled “Requirements”Requires Conn3x v0.6.5 or later and systemd on each host, with a package mirror the host can reach. Setting vm.overcommit_memory needs a host that owns its kernel settings: a VM or bare metal, or a privileged container.
Tested on
Section titled “Tested on”Rocky Linux 10, Fedora 45, Ubuntu 24.04 and Debian 13. CI applies examples/local to each on amd64, checks that the second plan has no changes, then uses valkey-cli to SET and GET a key with the password, checks a command without it gets NOAUTH, checks the example’s eviction policy and append-only file are on, restarts Valkey and reads the key back. The same ran by hand on arm64 containers, along with a password containing a space, a double quote and a backslash, and a move to port 6380 with persistence off and a new password, after which the second plan was clean.
On an Ubuntu 24.04 droplet and a Rocky Linux 10 droplet with SELinux enforcing, the same checks passed by hand, the Rocky one on port 6380. There the circuit labelled 6380 redis_port_t, Valkey ran confined as redis_t, and the audit log had no denials. Both came back after a reboot with Valkey running, the key still there and the second plan clean.
License
Section titled “License”MPL-2.0. See LICENSE. Valkey itself is BSD-3-Clause.
Source
Section titled “Source”- Repository: conn3xorg/circuit-valkey. This page covers v0.1.0; the repository’s tags list every release.
- Examples: examples/local.
- Related circuits: Firewall, PostgreSQL, MariaDB, MySQL, SQL Server, CouchDB, OpenSearch, SQLite.