Open Source
valkey-http-server

Published: October 26, 2026 · by Srinu Desetti (creator & maintainer)

valkey-http-server: A Self-Hostable HTTP Proxy for Valkey

valkey-http-server is a self-hostable HTTP proxy for Valkey — it lets edge and serverless clients reach Valkey over plain HTTP. It is the server half of a two-package pair: valkey-http is the fetch-based client you import in your edge function; this package is the endpoint that client talks to, running next to your own Valkey instance. I created and maintain both.

Packagevalkey-http-server (opens in a new tab) v0.1.1
LicenseMIT
Runtime dependencies2 — fastify (opens in a new tab), iovalkey (opens in a new tab)
Node.js≥ 18
Module typeESM
CLIvalkey-http-server binary included
Sourcegithub.com/webdevelopersrinu/valkey-http-server (opens in a new tab)

The Problem: Fetch-Only Runtimes Can't Speak RESP

Valkey speaks RESP over a raw TCP socket. That is a non-starter in a growing class of runtimes:

  1. Edge runtimes — Cloudflare Workers, Vercel Edge Functions, Deno Deploy — expose fetch, not arbitrary TCP sockets. A classic Valkey client simply cannot connect.
  2. Serverless functions — even where TCP is technically possible, a cold-started function paying a TCP + handshake round trip per invocation, and holding a persistent connection it will throw away milliseconds later, is the wrong shape. Stateless HTTP requests fit the model.

The established answer is an HTTP API in front of the datastore — and the established products are hosted ones. That solves the protocol problem but creates a new one: your cache now lives with a specific vendor, priced per request, reachable only over their endpoint, with your data on their infrastructure.

valkey-http-server is the same idea without the vendor: a small HTTP server you run yourself — next to your own Valkey, in your own network — built on Fastify for the HTTP layer and iovalkey (opens in a new tab) for the Valkey connection. The proxy holds the persistent TCP connection; your edge functions make stateless HTTP calls to it.

Edge function (fetch only)          Your infrastructure
┌──────────────────┐   HTTPS   ┌────────────────────┐   RESP/TCP   ┌────────┐
│ valkey-http      │ ────────▶ │ valkey-http-server │ ───────────▶ │ Valkey │
│ (client package) │           │ (this package)     │              │        │
└──────────────────┘           └────────────────────┘              └────────┘

Running It

The package ships a CLI, so the fastest path is npx — point it at your Valkey instance and it listens for HTTP:

terminal
# install and run
npm install valkey-http-server
npx valkey-http-server

# configured via environment
VALKEY_URL=valkey://localhost:6379 \
PORT=8080 \
AUTH_TOKEN=change-me \
npx valkey-http-server

For production you'll typically run it as a long-lived process in the same network as Valkey — a sidecar container, a systemd unit, or one more service in your compose file:

docker-compose.yml
services:
  valkey:
    image: valkey/valkey:8
    ports:
      - "6379:6379"

  valkey-http:
    image: node:20-alpine
    command: npx -y valkey-http-server
    environment:
      VALKEY_URL: valkey://valkey:6379
      PORT: 8080
      AUTH_TOKEN: ${VALKEY_HTTP_TOKEN}
    ports:
      - "8080:8080"
    depends_on:
      - valkey

Only the proxy needs to be reachable from the edge (behind TLS — put it behind your usual reverse proxy or load balancer). Valkey itself never has to leave the private network, which is a security posture classic clients can't give you when the caller lives on someone else's edge.

Talking to It

The intended consumer is the valkey-http client — a fetch-based package that works anywhere fetch does:

edge-function.js
import { createClient } from 'valkey-http'

const valkey = createClient({
  url: 'https://valkey-proxy.example.com',
  token: process.env.VALKEY_HTTP_TOKEN
})

export default async function handler(request) {
  const hits = await valkey.incr('pageviews')
  return new Response(JSON.stringify({ hits }))
}

But the server is just HTTP — any runtime, any language, plain curl included. Commands travel as a command-and-arguments payload, replies come back as JSON:

terminal
curl -X POST https://valkey-proxy.example.com/ \
  -H "Authorization: Bearer $VALKEY_HTTP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '["SET", "greeting", "hello"]'
# {"result":"OK"}

curl -X POST https://valkey-proxy.example.com/ \
  -H "Authorization: Bearer $VALKEY_HTTP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '["GET", "greeting"]'
# {"result":"hello"}

That shape is deliberate: the proxy doesn't maintain a per-command route table. It forwards the command through iovalkey, which means new Valkey commands work the day the server supports them — no proxy release required.

Design Notes — Decisions I Kept and Why

Fastify, not a hand-rolled HTTP layer

The proxy's job is to be a thin, fast pass-through. Fastify gives it JSON body parsing, schema-level validation at the trust boundary, and one of the lowest per-request overheads in the Node.js ecosystem — all things a hand-rolled http.createServer would end up re-implementing worse. The proxy adds latency to every single cache call, so the HTTP layer being cheap is the whole game.

iovalkey underneath, one connection shared

On the Valkey side, the proxy is an ordinary iovalkey consumer. One persistent connection (the thing edge functions can't have) is held by the proxy and shared across all incoming HTTP requests. Edge invocations stay stateless; the stateful part lives where state is cheap — a long-running Node.js process next to the database.

Self-hostable as the feature, not a fallback

Hosted HTTP-over-Redis/Valkey products treat self-hosting as the thing you graduate away from. This package inverts that: self-hosting is the product. Your data path is yours end to end — no per-request billing, no third-party endpoint in the middle of every cache read, and the proxy can sit on the same private network (or the same host) as Valkey, keeping the HTTP hop short.

Small on purpose

The published package is under 6 KB unpacked — a handful of files, two dependencies, Node's built-in test runner (node --test) instead of a test framework. At v0.1.1 it is early, and the small surface is what keeps it auditable: you can read the entire thing before you put it in front of your cache.

This is a solo project — I designed, built, and maintain valkey-http-server independently, as the server half of the valkey-http pair in the Valkey Node.js ecosystem.

What's Next?

This page covered the server half. The other half is the client your edge code actually imports — the fetch-based package that turns valkey.get('key') into the HTTP calls this proxy answers.

← Back to Open Source overview

Continue to valkey-http — the fetch-based edge client →


Frequently Asked Questions

Why not just use a hosted HTTP-over-Valkey service?

Hosted services solve the same protocol problem but put a vendor in your data path: per-request pricing, their endpoint in the middle of every cache read, your data on their infrastructure. valkey-http-server is the self-hosted equivalent — you run it next to your own Valkey, on your own network, and nothing about your cache leaves your control. If you already operate Valkey yourself, one more small Node.js process is a much smaller step than adopting a hosted platform.

Doesn't the extra HTTP hop make every call slower?

There is an extra hop, yes — that is the cost of reaching Valkey from a runtime that only has fetch. The design keeps it as small as possible: Fastify's low per-request overhead, a persistent iovalkey connection held by the proxy, and the recommendation to deploy the proxy on the same private network (or host) as Valkey so the added hop is a short one. For edge runtimes the comparison isn't "HTTP vs TCP" — it's "HTTP vs not being able to connect at all."

Can I call it without the valkey-http client?

Yes. The proxy speaks plain HTTP with JSON payloads, so any language or tool that can make an HTTP request can use it — curl, Python, Go, anything. The valkey-http client is the ergonomic path for JavaScript edge functions, but it holds no privileged protocol; it makes the same requests you could make by hand.

Is v0.1.1 production-ready?

It is an early release, and I say so plainly. What works in its favor: the package is tiny (under 6 KB unpacked, two dependencies), so the full surface is auditable in one sitting, and the heavy lifting is delegated to Fastify and iovalkey — both mature, widely deployed packages. Put it behind TLS, set an auth token, keep Valkey on the private network, and treat it like any other young piece of infrastructure: start with non-critical workloads.