# RAUDI: automatically updated images for the tools that have none

> A great many command line tools are distributed as source and expected to be installed from a package manager on your own machine, which is fine until you want them in a container, on a colleague's laptop, or in a continuous integration job that has to be reproducible. RAUDI is a repository of Dockerfiles plus the machinery to build them nightly and push them, so that the images exist without anybody having to maintain them by hand.

**cybersecsi/RAUDI** — A repo to automatically generate and keep updated a series of Docker images through GitHub Actions.

- Repository: https://github.com/cybersecsi/RAUDI
- Stars: 562 · Forks: 33
- Language: Python
- License: GPL-3.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/cybersecsi-raudi

## The problem, and the shape of the answer

RAUDI stands for Regularly and Automatically Updated Docker Images, and the readme's description of what it does is that it automatically generates and keeps updated a series of Docker images through GitHub Actions for tools that are not provided by the developers.

That last clause is the definition of the project's scope, and it is worth being precise about it, because there are two different reasons a tool has no official image. Some tools are distributed as source with an installation guide, and their maintainers have no interest in shipping containers. Others are distributed as binaries and do have an image, but it is stale, or built on an old base, or pinned to a version from two years ago. RAUDI is for the first category, and the readme says so explicitly: the list is of tools that do not have an official image provided by the developer.

The problem statement is stated in the plainest possible terms, and it is worth quoting because it is the whole motivation.

Every time a software is updated you need to update the Docker image if you want to use the latest features, and the dependencies are not working anymore. That is messy and time-consuming.

The second half of that is the sharper problem. It is not that you want the newest version for its own sake; it is that a container with pinned dependencies stops working when something it pinned ages out or a base image moves underneath it. A security tool whose dependencies no longer install is a tool you cannot use, and the person who notices is usually the person who needs it that afternoon.

So the answer is a schedule. The repository holds the images, a workflow rebuilds them regularly, and the images are pushed to a registry where they can be pulled by name and tag like any other.

And there are two ways to use it, which the readme states at the top of the usage section. You may fork the repository and use the GitHub workflow yourself, which gives you your own registry and your own schedule, or you can run the same machinery locally and manage its execution however you want. The second option is the one that makes the project useful to somebody who does not want to run a nightly workflow, and the whole tool is written so that the local path is a first-class citizen rather than a debugging aid.

## Forking it, and the two secrets

The fork path is described as a short list, and it is the path most people who want their own registry will take.

Two GitHub secrets must be set. One is the Docker Hub username and the other is the Docker Hub password or an application token. The readme is explicit that both are required, and that they are what allow the workflow to push images on your behalf.

Then there is a third thing that is easy to miss, because it is a file edit rather than a secret. The organisation variable in the tool's main file has to be edited, since it is configured to push to the account of the maintainers. So the three things a forker has to change are: two secrets in the repository settings, and one variable in a source file.

And that third one is the interesting part of the design. If the registry destination were a secret, the fork would be configuration. Because it is a variable in a file, the fork is a code change, which means it shows up in a diff, which means it is reviewable, and which means the default behaviour of the repository is unambiguous to anybody reading it. A reader who clones the repository can see exactly where the images are meant to go.

The readme's instruction for the rest is short. Go to the actions tab, enable the workflow for the forked repository, wait until midnight, and the workflow will do the work.

The midnight detail is worth a moment. The workflow is scheduled rather than triggered, so the fork does nothing on the first day and starts producing images on the first night. That is a deliberate design for a catalogue that must be rebuilt as upstream tools change: a schedule rather than a reaction, because a reaction means watching every upstream repository, and a schedule means the images are as fresh as yesterday and nobody has to be notified when something changes.

The cost of the schedule is that the images are at most a day behind, and that is the trade the project has made. For a security tool catalogue, a day of staleness is almost always acceptable, and the alternative, rebuilding whenever upstream pushes, produces a much noisier workflow and a much higher rate of failures.

## Running it locally, and the test setup

The local path has three requirements: Python 3.11 or newer, Docker with the BuildX builder, and the fast package manager for Python. The readme links the documentation for working with the builder.

BuildX is worth a sentence, because it is a specific requirement rather than a generic one. The older builder was fine for building one image, and the multi-platform builds that a catalogue of images wants need the newer one. A tool that depends on BuildX is a tool that has decided its images should be built for more than one architecture, which is a decision that shows up in the requirements rather than in the code.

The setup itself is three commands, and they are the modern ones.

```
git clone https://github.com/cybersecsi/RAUDI
cd RAUDI
uv sync --locked
```

The locked flag is in both the setup and every later command, and it is worth understanding what it means. It says: install exactly the versions recorded in the lock file, and refuse to run if the lock file and the manifest disagree. For a tool that builds containers from other people's repositories, that reproducibility is not a nicety, because a dependency that moves between runs can change the output of a build, and a build that produces a different image yesterday and today is a build you cannot reason about.

The test story is one line, and the readme explains where the test dependency comes from: the development dependencies, including the test runner, are installed by the same synchronisation command.

```
uv run --locked pytest -s
```

The dash for the output is the flag that stops the runner from capturing the standard output, which matters for a project whose tests may print things as part of the assertion. The other detail is that the tests run through the same package manager invocation as everything else, so a contributor who has cloned the repository and run one command has everything they need, with no separate virtual environment step to forget.

That combination, a lock file checked in, one command to install, and one command to test, is what makes the local path usable by somebody who is not the maintainer. The alternative, a set of instructions with a pip install and a list of versions, is the reason most project setup documentation fails.

## The execution modes, and the three options

The command line interface is a single entry point with a handful of modes, and the modes correspond to the things you actually want to do.

Normal execution builds all the tools, and the command says so. Single build builds one, and the argument is the name of a directory inside the tools folder. The readme is emphatic that the name must be the directory name, which tells you the tool's basic unit of configuration is a directory rather than an entry in a central list.

There is a mode for testing a single tool, and it is the one that reveals how the project is designed. The readme says that since a tests parameter has been added to the configuration structure of a tool, it is helpful to test whether the inserted commands do return a zero status code.

That is the key idea in the whole project. Each tool's directory contains a build recipe, and the recipe may include commands whose exit status tells you whether the build succeeded. A tool that compiles from source might have a test command that runs the binary, and a test command that returns non-zero means the image is broken. The tool's own test mode runs that and reports it, which means a broken image is caught before it is pushed rather than after somebody pulls it.

The list mode prints the available tools, and the bootstrap mode creates a new tool directory from one of the available templates. That last one is a good design decision: adding a tool means copying a template, filling it in, and testing it, rather than inventing a directory layout from scratch and getting the field names wrong.

Then there are three options, and the table of them is where the operational behaviour is.

One controls whether images are pushed to the registry, and it is off by default, which is right for a local run. One controls whether the check for an existing image is made against the registry or against the local Docker, and it is off by default, so a local run trusts what is on the machine. And one controls whether the build happens when an image with the same tag name has been found, and it is off by default, which means an already built image is not rebuilt.

Those defaults together define the tool's default posture: build everything, do not push, and rebuild even if you have the image.

## The catalogue, and the directory convention

The available tools are listed in a table with three columns: the name, the Docker image, and the source repository. And the images all follow the same pattern, an organisation prefix followed by a lower case version of the tool name, and the sources point at the upstream projects.

The list is long and it is a security toolkit, which tells you something about the audience: directory fuzzing tools, a certificate transparency scanner, a parameter fuzzer for web applications, a CMS scanner, a subdomain takeover checker, a toolkit for data extraction, an android bytecode converter, and a set of content discovery and directory brute force tools. All of them are the sort of thing that is distributed as a script or a source archive, none of them has an official container, and all of them are exactly the kind of tool you want pinned to a known-good version in a pipeline.

The name column and the image column differ in one visible way, which is that the image names are normalised to lower case while the upstream project names are not. That is the registry's naming rules, and it is a small detail that explains why the readme's requirement that the tool name match the directory name is worth stating exactly: the directory is the identifier, and the image name is derived from it.

The directory convention is therefore the API of the project. A tool is a directory under the tools folder, and that directory holds the recipe, the optional test commands, and whatever else the recipe needs. The bootstrap mode exists to create such a directory correctly, and the templates directory in the repository is where those starting points live.

That design has a consequence that is easy to miss. Because a tool is a directory and not a class, adding one is a pull request that adds files rather than code, and the review of that pull request is a review of a recipe. There is no plugin registry, no entry point to register, and no version negotiation between the tool and the builder. The tool either builds or it does not, and the test mode tells you which.

The repository layout matches: a source directory for the program, a tools directory for the catalogue, a templates directory for the starting points, a test directory, a sample environment file, a log file, the two usual governance documents, and the lock file. There is also a directory for the GitHub workflows, which is where the scheduled build lives.

## The manifest, and what the dependencies say

The project manifest is short, and every line of it is a decision.

The package requires Python 3.11 or newer and is licensed under the third version of the GNU General Public Licence, with the only-variant suffix, which means there is no alternative licensing route. The version is at the very start of its life, at zero point one.

The dependencies are four, and each one is pinned to an exact version rather than a range, which is a strong statement in a project whose job is to build things reproducibly. One library loads the environment file. One is an interface to the container engine, which is how the program talks to Docker. One is a library for interactive prompts, which is what the bootstrap wizard uses when it asks you questions. And one is the HTTP client.

The container engine library is the interesting choice. A project that shells out to the Docker command line works, but a project that drives the engine through a typed client is faster, gives better error messages, and can ask questions about the engine's state. The readme's mention of BuildX in the requirements and of checking against a remote registry rather than a local one are both capabilities of that interface rather than of the command line.

The interactive prompt library is the other one that says something. A tool that only built images would not need it. It is there because the bootstrap mode is a wizard, and because a wizard is the difference between adding a tool in five minutes and adding one in an hour.

The build system is the same package manager, with a pinned version of the build backend and, separately, a requirement that the package manager itself be exactly that version. Pinning the tool is unusual and slightly awkward, and it is the sort of thing a maintainer does after a tooling upgrade changed the output of a build for them. Given that this project's output is container images, that a change in the build tool can change the built artefact is a real concern rather than a hypothetical one.

The entry point is a single console script named after the project, pointing at the command line module's main function, and the development group contains the test runner at a pinned version.

And the sample environment file shows the three settings the program reads: the GitHub token, a flag for whether it is running inside the workflow, and a flag for whether to show the progress of the Docker build. The first of those is the rate limit protection, and the readme explains the arithmetic with real numbers: unauthenticated users are allowed sixty requests an hour, authenticated ones fifteen thousand, and the advice is to add a token without any scope, since everything the program does is read-only against the source repositories.

## Conclusion

RAUDI fits a security engineer who needs a reproducible container for a tool that ships only as source, and who does not want to be the person who rebuilds that container every time the tool changes. It does not fit somebody who wants a general purpose image builder, because the whole design is a catalogue with a schedule attached. Verify first that the tool you want is in the catalogue or that you can write its image yourself, that the Docker Hub organisation variable in the tool's main file points at your own account rather than the maintainer's, that you have a registry credential available as a secret, and that the rate limit arithmetic in the readme is not going to bite you, since the whole catalogue is fetched from source repositories on every run.

## FAQ

### What does the RAUDI project do?

It automatically generates and keeps updated Docker images for tools that their developers do not ship as images, using a scheduled GitHub Action or a local command. The images are pushed to a registry so that a tool can be pulled by name and tag like any other container.

### What do I need to set up to fork RAUDI?

Two GitHub secrets, the Docker Hub username and the Docker Hub password or token, so the workflow can push on your behalf, and an edit to the organisation variable in the tool's main file, because it points at the maintainer's registry by default. Then you enable the workflow in the actions tab and wait for the nightly run.

### How do I add a new tool to RAUDI?

Create a directory under the tools folder, using the bootstrap command to start from one of the available templates. The directory name is the tool's identifier and must be used when building that tool on its own. Adding a tool is a pull request that adds a recipe rather than code.

### What is the test mode of RAUDI for?

Each tool's configuration can carry a test parameter listing commands, and the test mode runs them for a single tool to check that they return a zero status code. That means a build whose own test fails is caught before the image is pushed rather than after somebody pulls it.

### What are the three options of the RAUDI command line?

One controls whether images are pushed to the registry, one controls whether the existence of an image is checked against the registry or against the local Docker, and one controls whether the build runs at all when an image with the same tag already exists. All three are off by default, so a local run builds everything, pushes nothing, and skips rebuilding an image whose tag already exists.

## Sources

- [cybersecsi/RAUDI on GitHub](https://github.com/cybersecsi/RAUDI)
- [Issues](https://github.com/cybersecsi/RAUDI/issues)
- [License: GPL-3.0](https://github.com/cybersecsi/RAUDI/blob/main/LICENSE)
- [README](https://github.com/cybersecsi/RAUDI/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/cybersecsi-raudi
