Redis C redis/redis

Getting Started with Your First Contribution to Redis

Walk from repo orientation to a running redis-server in seven stops, covering the Tcl test harness, the command-definition pattern, and the tri-license a new contributor must accept.

7 stops ~18 min Verified 2026-05-04
What you will learn
  • Where the README points first-time readers, and why CONTRIBUTING.md is the next file to open
  • How the root Makefile dispatches to src/Makefile and modules/Makefile via SUBDIRS
  • What main() in src/server.c does on entry, including the REDIS_TEST built-in test runner
  • How the runtest shell script locates a tclsh binary and hands control to tests/test_helper.tcl
  • The shape of a JSON file in src/commands/, which is how new Redis commands are registered
  • What the RSALv2/SSPLv1/AGPLv3 tri-license requires of every contributor before code is merged
  • Why the event loop in src/ae.c is the next file to read once orientation is complete
Prerequisites
  • Comfortable reading C (you do not need to write it, just trace the logic)
  • Familiarity with make and a POSIX shell
  • No prior Redis source knowledge required
1 / 7

README and Repo Orientation

README.md:1

The first ten lines of the README route every reader to the right next page: install, build, contribute, or full docs.

The Redis README opens by routing readers, not by selling features. Three of the four bullets answer questions a first-time contributor will ask within the first hour: where to start reading, where to build from source, and where the contribution rules live. The fourth bullet hands off to the hosted docs at redis.io/docs for usage questions that do not belong in the source repo. The link to CONTRIBUTING.md on line 8 is the one that matters for this tour, because every patch you write needs the agreement that file describes. Read this header carefully and you will know which of three paths you are on before you open another file.

Key takeaway

The README opens as a routing table; the bullet that points at CONTRIBUTING.md is the one a new contributor must follow before writing any code.

[![codecov](https://codecov.io/github/redis/redis/graph/badge.svg?token=6bVHb5fRuz)](https://codecov.io/github/redis/redis)

This document serves as both a quick start guide to Redis and a detailed resource for building it from source.

- New to Redis? Start with [What is Redis](#what-is-redis) and [Getting Started](#getting-started)
- Ready to build from source? Jump to [Build Redis from Source](#build-redis-from-source)
- Want to contribute? See the [Code contributions](#code-contributions) section
  and [CONTRIBUTING.md](./CONTRIBUTING.md)
- Looking for detailed documentation? Navigate to [redis.io/docs](https://redis.io/docs/)

2 / 7

Build via the Root Makefile

Makefile:1

The root Makefile is a dispatcher: it forwards every target to src/Makefile, and to modules/Makefile when BUILD_WITH_MODULES=yes.

A repository this large needs a clear answer to make at the top, and Redis gives one in nineteen lines. The header comment is honest: the real build logic lives in src/Makefile. SUBDIRS = src sets the default; the BUILD_WITH_MODULES=yes branch appends modules for builds that include Bloom filter, Cuckoo filter, and the other module-backed types listed in the README. The .DEFAULT rule is the dispatcher: any target you pass on the command line is forwarded to each subdir via a per-directory $(MAKE) -C. So make runs all in src; make test runs test in src; make install uses the explicit rule on line 16 for the same effect.

Key takeaway

The root Makefile dispatches to src/Makefile for all targets and adds modules/Makefile only when BUILD_WITH_MODULES=yes.

# Top level makefile, the real stuff is at ./src/Makefile and in ./modules/Makefile

SUBDIRS = src
ifeq ($(BUILD_WITH_MODULES), yes)
	ifeq ($(MAKECMDGOALS),32bit)
    	$(error BUILD_WITH_MODULES=yes is not supported on 32 bit systems)
	endif
	SUBDIRS += modules
endif

default: all

.DEFAULT:
	for dir in $(SUBDIRS); do $(MAKE) -C $$dir $@; done

install:
	for dir in $(SUBDIRS); do $(MAKE) -C $$dir $@; done

.PHONY: install
3 / 7

redis-server Entry: main() in src/server.c

src/server.c:7822

Process entry for redis-server. Before normal startup, main() checks for a redis-server test invocation and dispatches to the in-process unit-test runner.

Redis is a single binary, and main() is the only entry point. The first surprise on opening it is that the very first branch is not server startup at all: when compiled with REDIS_TEST, the binary doubles as a unit-test runner. Calling ./redis-server test all walks the static redisTests table and runs each test in process, with optional accuracy, large-memory, and valgrind switches passed as additional arguments. monotonicInit() on line 7828 has to run before any test that depends on Redis dict rehashing timing. Past this block the function falls through into the actual server bootstrap: argument parsing, initServerConfig(), initServer(), then aeMain() on the event loop.

Key takeaway

The redis-server binary serves two roles from one main(): a server, and (with REDIS_TEST) an in-process unit-test runner dispatched on the first argument.

int main(int argc, char **argv) {
    struct timeval tv;
    int j;
    char config_from_stdin = 0;

#ifdef REDIS_TEST
    monotonicInit(); /* Required for dict tests, that are relying on monotime during dict rehashing. */
    if (argc >= 3 && !strcasecmp(argv[1], "test")) {
        int flags = 0;
        for (j = 3; j < argc; j++) {
            char *arg = argv[j];
            if (!strcasecmp(arg, "--accurate")) flags |= REDIS_TEST_ACCURATE;
            else if (!strcasecmp(arg, "--large-memory")) flags |= REDIS_TEST_LARGE_MEMORY;
            else if (!strcasecmp(arg, "--valgrind")) flags |= REDIS_TEST_VALGRIND;
            else if (!strcasecmp(arg, "--verbose")) flags |= REDIS_TEST_VERBOSE;
4 / 7

The Test Harness: runtest

runtest:1

A 14-line shell script that locates a working tclsh and hands control to tests/test_helper.tcl, which drives the integration test suite.

Redis tests are written in Tcl, which is a deliberate inheritance from antirez's first Redis prototype. runtest exists because Tcl ships with version-suffixed binaries on most distributions: tclsh8.6, tclsh9.0, and so on. The script tries each known version in order and remembers the last one it finds. If none exists, it prints a clear error and exits with status 1, which is the behaviour you want on a fresh machine without Tcl installed. The final line passes every command-line argument through ${@} to tests/test_helper.tcl, so any selector argument such as a single test path reaches the test runner unchanged. Two sister scripts, runtest-cluster and runtest-sentinel, follow the same pattern for the cluster and Sentinel suites.

Key takeaway

Tcl drives every Redis integration test; runtest finds a working tclsh, then forwards every flag to tests/test_helper.tcl.

#!/bin/sh
TCL_VERSIONS="8.5 8.6 8.7 9.0"
TCLSH=""

for VERSION in $TCL_VERSIONS; do
	TCL=`which tclsh$VERSION 2>/dev/null` && TCLSH=$TCL
done

if [ -z $TCLSH ]
then
    echo "You need tcl 8.5 or newer in order to run the Redis test"
    exit 1
fi
$TCLSH tests/test_helper.tcl "${@}"
5 / 7

Where New Commands Go: src/commands/

src/commands/echo.json:1

Every Redis command is registered by a JSON file in src/commands/; echo.json shows the minimum a new command must declare.

If your first contribution adds a command, the file you write lives in src/commands/. echo.json is the smallest realistic example. arity: 2 means exactly one argument is required; function: "echoCommand" names the C handler that the build wires up for you. command_flags declares runtime guarantees: FAST says the command is bounded constant-time, LOADING and STALE let it run during dataset load and on stale replicas. acl_categories drives ACL rule matching. reply_schema is the contract a client sees on the wire, and is also what the documentation site renders. Adding a command means writing the JSON, writing the matching somethingCommand in C, and letting the build regenerate the dispatch table.

Key takeaway

A command JSON declares arity, the C handler, command flags, ACL category, and the reply schema; the build links all of it into the dispatch table.

{
    "ECHO": {
        "summary": "Returns the given string.",
        "complexity": "O(1)",
        "group": "connection",
        "since": "1.0.0",
        "arity": 2,
        "function": "echoCommand",
        "command_flags": [
            "LOADING",
            "STALE",
            "FAST"
        ],
        "acl_categories": [
            "CONNECTION"
        ],
        "reply_schema": {
            "description": "The given string",
            "type": "string"
        },
        "arguments": [
            {
                "name": "message",
                "type": "string"
            }
        ]
    }
}
6 / 7

CONTRIBUTING.md and the Tri-License

CONTRIBUTING.md:1

The first seventeen lines of CONTRIBUTING.md bind every patch to the RSALv2/SSPLv1/AGPLv3 tri-license through the Redis Software Grant and Contributor License Agreement.

Redis stopped being BSD-licensed in March 2024, and the licence on every contribution changed with it. The first sentence of CONTRIBUTING.md is the one that matters: any code you submit is accepted only under the Redis Software Grant and Contributor License Agreement and falls under the tri-license of RSALv2, SSPLv1, and AGPLv3. The CLA itself, headed at line 7, is what you grant to Redis Ltd.; section 3 (further down the file) is the perpetual copyright licence, and section 4 is the patent grant. Read these before you open a pull request, because the project will not merge code from a contributor who has not accepted them. The trademark and issue-routing notes appear later in the same file.

Key takeaway

Every Redis contribution is bound to the RSALv2/SSPLv1/AGPLv3 tri-license through the CLA at the top of CONTRIBUTING.md; accept it before opening a pull request.

By contributing code to the Redis project in any form you agree to the Redis Software Grant and
Contributor License Agreement attached below. Only contributions made under the Redis Software Grant
and Contributor License Agreement may be accepted by Redis, and any contribution is subject to the
terms of the Redis tri-license under RSALv2/SSPLv1/AGPLv3 as described in the LICENSE.txt file included in
the Redis source distribution.

# REDIS SOFTWARE GRANT AND CONTRIBUTOR LICENSE AGREEMENT

To specify the intellectual property license granted in any Contribution, Redis Ltd., ("**Redis**")
requires a Software Grant and Contributor License Agreement ("**Agreement**"). This Agreement is for
your protection as a contributor as well as the protection of Redis and its users; it does not
change your rights to use your own Contribution for any other purpose permitted by this Agreement.

By making any Contribution, You accept and agree to the following terms and conditions for the
Contribution. Except for the license granted in this Agreement to Redis and the recipients of the
software distributed by Redis, You reserve all right, title, and interest in and to Your
Contribution.
7 / 7

Where to Read Next: src/ae.c and the Event Loop

src/ae.c:492

aeMain() is the loop that drives every Redis command after orientation is complete; the architecture tour on this page traces it end to end.

Once orientation is done, the next file to open is src/ae.c. aeMain() is six lines of executable code and is also the heart of the server: it spins until eventLoop->stop is set, calling aeProcessEvents() with the flags that ask for I/O events plus the before-sleep and after-sleep hooks. There are no threads on the hot path, so commands run serially and the source can be read without tracking concurrent execution. The architecture tour on this Explore page picks up here and walks through how a client byte becomes a parsed command, a dict lookup, and a reply, in roughly twenty-five minutes. Replication, persistence, cluster, streams, sentinel, pub/sub, data structures, and the RESP protocol each have their own deeper tour.

Key takeaway

aeMain() is six lines of code and the entry to every other Redis subsystem; the architecture tour on this page traces what happens inside each aeProcessEvents() call.

void aeMain(aeEventLoop *eventLoop) {
    eventLoop->stop = 0;
    while (!eventLoop->stop) {
        aeProcessEvents(eventLoop, AE_ALL_EVENTS|
                                   AE_CALL_BEFORE_SLEEP|
                                   AE_CALL_AFTER_SLEEP);
    }
}

char *aeGetApiName(void) {
    return aeApiName();
}

void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep) {
Your codebase next

Create code tours for your project

Intraview lets AI create interactive walkthroughs of any codebase. Install the free VS Code extension and generate your first tour in minutes.

Install Intraview Free