12-Factor Web Development: Support for Valkey

Web App Development: Support for Valkey

Hi everyone,

We would like to announce that we have added support for Valkey integration for all supported frameworks (Flask, Django, FastAPI, Go, Express.js, and Spring Boot)!

Valkey is a high-performance key-value data store, created as a drop-in alternative to Redis following Redis’s license change. With this new integration, we support the valkey_client interface, which is provided by the Valkey charm. Below is an example of how you can charm a simple Flask app to integrate with the Valkey charm for key-value storage.

Valkey Flask Application

In this post, we will see how we can charm a Flask app to integrate it with the Valkey charm to store and retrieve data. We will start by setting up our development environment:

multipass launch --cpus 4 --disk 50G --memory 4G --name charm-dev 24.04
multipass shell charm-dev

sudo snap install concierge --classic
sudo concierge prepare -p dev

juju add-model valkey-demo

Now that we have our environment ready, let’s deploy Valkey:

juju deploy valkey --channel 9/edge --trust --revision 40

Now that we have Valkey deploying in the background, we can start writing the Flask application.

First, create the folder for our application:

mkdir flask-valkey
cd flask-valkey

Let’s create the app.py file with the following contents:

import os
import valkey
from flask import Flask

app = Flask(__name__)

def get_valkey_uri():
    return os.environ.get("VALKEY_DB_CONNECT_STRING", "")

@app.route("/set/<key>/<value>")
def set_key(key, value):
    uri = get_valkey_uri()
    if not uri:
        return "Valkey not configured", 503
    v = valkey.Valkey.from_url(uri)
    v.set(key, value)
    return "OK"

@app.route("/get/<key>")
def get_key(key):
    uri = get_valkey_uri()
    if not uri:
        return "Valkey not configured", 503
    v = valkey.Valkey.from_url(uri)
    return v.get(key) or ""

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=os.environ.get("APP_PORT", "8080"))

Then, create a requirements.txt file:

flask
valkey

Now we need to create the OCI image for the Flask application:

# Deploy a local registry in the cluster
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: registry
  namespace: kube-system
  labels:
    app: registry
spec:
  selector:
    matchLabels:
      app: registry
  template:
    metadata:
      labels:
        app: registry
    spec:
      containers:
        - name: registry
          image: registry:2
          ports:
            - containerPort: 5000
          volumeMounts:
            - name: registry-storage
              mountPath: /var/lib/registry
      volumes:
        - name: registry-storage
          emptyDir: {}
      hostNetwork: true
      tolerations:
        - operator: "Exists"
EOF

rockcraft init --profile flask-framework --name flask-valkey
rockcraft pack --use-lxd

# Push image to local registry
sudo rockcraft.skopeo --insecure-policy copy oci-archive:flask-valkey_0.1_amd64.rock docker://localhost:5000/flask-valkey:0.1 --dest-tls-verify=false

Once we have the OCI Image pushed to the local registry, we can create the charm:

mkdir charm
cd charm

charmcraft init --profile flask-framework --name flask-valkey

We need to modify the charmcraft.yaml file to add the Valkey relation. Add the following lines to the requires section:

requires:
  valkey:
    interface: valkey_client
    optional: false
    limit: 1

We also need to update the requirements.txt file generated by charmcraft init to use ops >= 3 to ensure compatibility with the latest version of paas-charm

ops >= 3
paas-charm>=1.0,<2

Then, we can continue with creating the charm:

charmcraft pack

Once the charm is created, we can deploy it to our model:

# Deploy the charm
juju deploy ./flask-valkey_amd64.charm --resource flask-app-image=localhost:5000/flask-valkey:0.1

# Integrate with Valkey
juju integrate flask-valkey:valkey valkey:valkey-client

Now that our charm is deployed and integrated, we can test the Valkey integration:

# Get the flask-valkey container ip
UNIT_IP=$(juju show-unit flask-valkey/0 | yq '.flask-valkey/0.address')

# Set a key-value pair
curl $UNIT_IP:8000/set/mykey/myvalue

# Get the value back
curl $UNIT_IP:8000/get/mykey
# Expected output: myvalue

That’s all it takes to enable Valkey integration for our webapp! We have deployed the Flask application and integrated it with the Valkey charm.


Environment Variables Reference

When integrated with Valkey, your application will receive the following environment variables:

Variable Description
VALKEY_DB_CONNECT_STRING Full connection URL with embedded credentials
VALKEY_DB_SCHEME URL scheme (e.g., “valkey”)
VALKEY_DB_NETLOC Network location part of the URL
VALKEY_DB_PATH URL path component
VALKEY_DB_PARAMS URL parameters
VALKEY_DB_QUERY URL query string
VALKEY_DB_FRAGMENT URL fragment
VALKEY_DB_USERNAME Username for authentication
VALKEY_DB_PASSWORD Password for authentication
VALKEY_DB_HOSTNAME Valkey server hostname
VALKEY_DB_PORT Valkey server port
VALKEY_DB_NAME Database name/index
VALKEY_DB_TLS TLS enabled flag
VALKEY_DB_MODE Valkey mode (e.g., “sentinel”)
VALKEY_DB_TLS_CA TLS CA certificate
VALKEY_DB_VERSION Valkey server version