FORGE
Lightweight Deployment CLI
Deploy containerized applications from any Git repository with a single command. No YAML. No bloat. Written in C from scratch to understand deployment pipelines from the ground up.
Built Different
Most deployment tools are optimised for teams managing production infrastructure. FORGE is optimised for understanding. It exposes the raw mechanics of cloning, building, and running containers — and wraps them in a C program small enough to read in an afternoon.
One subcommand to clone, build, and start a container. No dashboards, no GUIs, no abstraction hiding what's actually happening.
FORGE shells out to your existing Docker daemon. It doesn't reinvent container management — it just automates the repetitive commands you already know.
Pass a Git URL. FORGE infers the image name, build context, and container name from the repository. Sensible defaults you can always override with flags.
Pure ANSI C with POSIX extensions. No dependencies outside libc. The entire source compiles in under a second and fits in a single translation unit.
Every design decision prioritises clarity. Reading the source is the documentation. FORGE exists to be understood, not just used.
The compiled binary is a few dozen kilobytes. FORGE adds no runtime overhead; all the work is done by git and docker which you already have installed.
Zero to Running
Three steps: install prerequisites, compile FORGE, deploy a repo. No package manager, no runtime, no configuration file.
Step 1 — Prerequisites
You need git, docker, and a C compiler available on your PATH. That's it.
$ git --version
git version 2.43.0
$ docker --version
Docker version 25.0.3, build 4debf41
$ gcc --version
gcc (Ubuntu 13.2.0) 13.2.0
Step 2 — Build FORGE
$ git clone https://github.com/gauranga18/forge.git
$ cd forge
$ make
✓ forge compiled successfully (18 KB)
Step 3 — Deploy
$ forge deploy https://github.com/user/my-app.git
Cloning https://github.com/user/my-app.git ...
Building my-app:latest ...
Starting container my-app ...
✓ my-app is running on port 8080
forge binary to a directory on your PATH (e.g. /usr/local/bin) so you can run it from anywhere.
Install FORGE
FORGE ships as source. You compile it once on your machine and get a standalone native binary with no runtime dependencies.
Linux / macOS
# Clone and compile
$ git clone https://github.com/gauranga18/forge.git ~/forge
$ cd ~/forge && make
# Optional: install system-wide
$ sudo cp forge /usr/local/bin/forge
$ forge --version
forge v1.0
Windows (WSL2)
# Inside a WSL2 Ubuntu shell
$ sudo apt-get install -y gcc make git docker.io
$ git clone https://github.com/gauranga18/forge.git
$ cd forge && make
✓ forge compiled successfully
Manual Compile (no Make)
$ gcc -O2 -Wall -Wextra -pedantic \
src/forge.c \
-o forge
forge. On macOS and Windows use Docker Desktop; on Linux start the daemon with sudo systemctl start docker.
Commands
All FORGE functionality lives under a single binary. The first positional argument is always the subcommand. Additional positional arguments and flags follow.
Syntax
forge <subcommand> [arguments] [flags]
Subcommand Reference
| Command | Argument | Description |
|---|---|---|
| deploy | <git-url> required |
Clone the repository, build a Docker image from its Dockerfile, and start a container. All three steps in one command. |
| build | <git-url> required |
Clone and build the Docker image only. Does not start a container. Useful for verifying a Dockerfile before deploying. |
| run | <image> required |
Start a container from a locally available image. Skips the clone and build steps. |
| stop | <name> required |
Stop a running container by name. |
| rm | <name> required |
Remove a stopped container. Use --force to remove a running container. |
| logs | <name> required |
Stream stdout/stderr logs from a running or stopped container. |
| status | <name> optional |
Show the status of a named container, or list all containers managed by FORGE if no name is given. |
| version | — | Print the FORGE version string and exit. |
| help | [subcommand] optional |
Show help for FORGE or for a specific subcommand. |
Flags & Options
Flags are optional and always follow the subcommand and its required arguments. Unrecognised flags cause FORGE to exit with a non-zero status and print usage.
Global flags
--verbose.deploy / build flags
.gitlatest8080/tmp/forge-<name>Examples
Real-world invocation patterns covering the most common workflows.
Deploy with custom port
$ forge deploy https://github.com/user/api.git \
--port 3000 \
--detach
Deploy a specific branch
$ forge deploy https://github.com/user/app.git \
--branch staging \
--name app-staging
Passing environment variables
$ forge deploy https://github.com/user/web.git \
--env NODE_ENV=production \
--env DATABASE_URL=postgres://localhost/mydb
Dry run to inspect actions
$ forge deploy https://github.com/user/app.git --dry-run
[dry-run] git clone https://github.com/user/app.git /tmp/forge-app
[dry-run] docker build -t app:latest /tmp/forge-app
[dry-run] docker run -p 8080:8080 --name app app:latest
✓ dry run complete — no changes made
View logs and stop
# Stream logs
$ forge logs app
# Stop the container
$ forge stop app
# Remove it
$ forge rm app
Redeploy (force replace)
# Stop/remove existing container and redeploy
$ forge deploy https://github.com/user/app.git --force
Stopping existing container app ...
Removing container app ...
Cloning https://github.com/user/app.git ...
Building app:latest ...
Starting container app ...
✓ app is running on port 8080
Architecture
FORGE follows a deliberately simple pipeline. Each subcommand maps to a small
C function that constructs a shell command string and hands it to execvp()
(or popen() where output capture is needed). There is no intermediate
representation, no plugin system, no async runtime.
Deploy Pipeline
A hand-written parse_args() function walks argv and populates a ForgeConfig struct. Unknown flags abort early with a usage message and exit code 1.
If --name was not provided, FORGE extracts the last path segment of the git URL, strips a trailing .git, and uses that as both image name and container name.
FORGE calls git clone [--branch <b>] <url> <dir> via execvp(). The parent process waits for the child with waitpid() and checks the exit code.
Calls docker build -t <name>:<tag> <dir>. Build output is piped to FORGE's stdout so you can see layers being processed in real time.
Assembles a docker run command string from the parsed flags (--port, --env, --detach) and executes it. The container name is tracked in a local state file under ~/.forge/.
A newline-delimited text file at ~/.forge/containers records each container name managed by FORGE. The status and rm subcommands read this file to enumerate containers.
git and docker, it inherits all their capabilities and limitations. Any valid Dockerfile works; any git transport protocol supported by your git installation works.
Source Layout
The entire implementation lives in a single C source file. The project is structured so you can read it top-to-bottom as a tutorial.
forge/
├── src/
│ └── forge.c # entire implementation (~600 lines)
├── Makefile # default target: compile with -O2 -Wall
├── README.md
└── LICENSE
# Runtime paths (created on first use)
~/.forge/
└── containers # plain-text container registry
Key C Functions
| Function | Signature | Responsibility |
|---|---|---|
| main | int main(int argc, char **argv) |
Entry point. Routes to the correct subcommand handler. |
| parse_args | ForgeConfig parse_args(int, char**) |
Walks argv and fills a ForgeConfig struct. Prints usage on error. |
| derive_name | char *derive_name(const char *url) |
Extracts repo name from git URL. Strips trailing .git. |
| run_cmd | int run_cmd(char *const argv[]) |
Fork-exec wrapper. Waits for child and returns exit code. |
| cmd_deploy | int cmd_deploy(ForgeConfig *) |
Orchestrates clone → build → run pipeline. |
| state_add | void state_add(const char *name) |
Appends a container name to ~/.forge/containers. |
Who Uses FORGE
FORGE targets developers who want to understand what deployment tools actually do — not just use them as black boxes.
Trace every line of C source to understand how a deployment CLI actually invokes git and Docker under the hood.
Spin up personal projects and prototypes from any git URL without creating config files or memorising docker run options.
Learn infrastructure automation patterns — clone, build, run, log, stop, remove — with a tiny, auditable codebase.
Reproduce a cloud deployment locally. FORGE's --dry-run flag lets you inspect the exact commands before touching anything.
Tech Stack
FORGE has exactly zero library dependencies beyond what ships with your operating system. The only tools required to build it are a C compiler and Make.
Frequently Asked
docker build on the cloned directory and expects a
Dockerfile at the repository root. If none is found, Docker will exit with
an error and FORGE will propagate it.
git clone.
If your git credentials (SSH keys, netrc, credential helpers) are configured,
private repos work automatically. FORGE itself does not handle authentication.
8080 to the container's exposed port.
Use --port <n> to choose a different host port. The container port is
whatever EXPOSE declares in the Dockerfile.
fork, execvp, waitpid)
that are not available on native Windows. WSL2 with Ubuntu is the recommended
path on Windows.
rm /usr/local/bin/forge) and optionally remove
the state directory (rm -rf ~/.forge). FORGE creates no other files
on your system beyond what you explicitly point it at.