Product

Overview

How it works Watch it work All 35 features Screenshots How ForgeRepo™ is secured

Keep bad packages out

Malware scanning Typosquat detection Dependency confusion protection Cooling off new releases Allow lists and block lists

When something goes wrong

Kill switch Who has it Lockdown and degraded modes Vulnerability monitoring

Developers and approvals

Requests and auto approve Check, walk and review Waivers Dry run
Formats

Languages

Private npm registry PyPI proxy and private index Private NuGet feed and proxy Maven repository proxy RubyGems mirror Composer and Packagist proxy

Apple, containers and Linux

CocoaPods CDN mirror Swift package registry Docker registry mirror RPM mirror for dnf and yum APT mirror for Debian and Ubuntu

Use it as

An npm firewall Artifacts, SBOMs and lifecycle More than one node SSO, roles and allow lists
Learn

Guides

Learn secure development Secure coding Secure pipelines Unsafe vs safe Security and development

Supply chain

Supply chain attacks, 2016 to 2026 Software supply chain security
Compare Side by side, all four ForgeRepo™ vs JFrog ForgeRepo™ vs Sonatype ForgeRepo™ vs Cloudsmith
Docs

Start

Getting started Download All documentation Questions

For developers

npm setup pip setup docker login Push a Docker image CI basics

For administrators

First setup Rules and their order Six incidents, walked through Backup and upgrade
Pricing Install it

Getting started

From a bare server to your first blocked install

Six steps, and the first two take about 96 seconds. Every player on this page replays a real session: the installer run on a clean Ubuntu server, and npm, pip and docker run against a real ForgeRepo™. Press play, or read the commands and go.

  1. Check what you need
  2. Run the installer
  3. Put a reverse proxy in front
  4. Sign in and set the policy
  5. Point npm, pip and docker at it
  6. Turn on the extras

Step 1

What you need

A Linux server

Ubuntu 22.04 or 24.04 LTS are tested. Debian and the RHEL family use the same steps and should work. It runs as one container, and ClamAV, if you add it, holds about 1 GB of memory of its own.

A name and a certificate

Something like packages.example.com, and a proxy for TLS: nginx, Caddy, HAProxy or a cloud load balancer. A worked nginx file ships in the repository.

Disk for the cache

About 2 GB to start, and room to grow, since every approved version stays cached. Or put the cache in S3 or Azure later.

No Node, no MySQL and no npm on the host. Everything is in the image. The installer brings Docker and the compose plugin if they are missing.

Step 2

Run the installer

Two commands. Put your own address in place of packages.example.com.

curl -fsSLO https://raw.githubusercontent.com/hackrange/forgerepo/master/setup.sh
sudo bash setup.sh --url https://packages.example.com

It installs Docker if it is missing, puts the project in /data/docker/forgerepo, makes up a strong admin password and a break glass key, builds the image, starts it, and waits for the database. The password is printed once and written to .env, and the portal makes you change it at the first sign in.

Safe to run twice

It never overwrites an existing .env and never touches your data. Run it again any time.

Options, and the long way
--url https://…the address developers will use
--dir /opt/npm-repoinstall somewhere else
--port 4444host port to publish on, always on 127.0.0.1
--name npm-repocontainer name, to run two on one box
--no-startset it all up but do not start it
--db-host …join a shared MariaDB or RDS as a second node
--upgradepull, rebuild, restart, keeping .env and data

Or by hand:

git clone https://github.com/hackrange/forgerepo.git forgerepo
cd forgerepo
cp .env.example .env
$EDITOR .env          # PUBLIC_URL at least
docker compose up -d --build
docker compose logs | grep "temporary password"
root@registry-01: ~
The real install 0:00 / 0:00

Replay of setup.sh on a clean Ubuntu 24.04 server on 18 September 2026, 96 seconds end to end. Waits are shortened and marked, and the password and key are masked.

Step 3

Put a reverse proxy in front

The container listens on 127.0.0.1:4444. Your proxy terminates TLS and passes everything through. The complete file, with the HTTP to HTTPS redirect, certificate issuance and a log format that redacts break glass keys, is nginx/npm-repo.conf.example in the repository.

Two details that matter

Set X-Forwarded-For to $remote_addr, never append to it, because both network allow lists read that header. And keep the app port where only the proxy can reach it, so nobody can send the header themselves.

/etc/nginx/sites-enabled/packages.conf
server {
    listen 443 ssl;
    http2 on;
    server_name packages.example.com;
    ssl_certificate     /etc/letsencrypt/live/packages.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/packages.example.com/privkey.pem;
    client_max_body_size 32m;

    location / {
        proxy_pass http://127.0.0.1:4444;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        proxy_set_header X-Forwarded-For   $remote_addr;   # set, never append
        proxy_set_header X-Forwarded-Proto https;
        proxy_read_timeout 300s;
        proxy_max_temp_file_size 0;
    }
}

Step 4

Sign in and set the policy

Open https://packages.example.com/_admin/ and sign in as admin. Here is the first ten minutes, on the real screens.

https://packages.company.internal/_admin/
Step 1

First sign in to first approval 0:00 / 0:00

A guided walkthrough built from real screenshots, not recorded video. Use the arrows, or the left and right keys, to go at your own pace.

Start in audit only mode

Tick Audit only under Settings, Policy, and ForgeRepo™ serves everything while opening a request for whatever no rule covers, with the exact versions used. After a week or two, approve what your teams really use, switch audit off, and the whitelist is not a cold start.

The rest of the checklist

Public URL, name and branding, which package types to serve, upstream registries, require tokens, the portal allow list, email and SSO. It is all in First setup checklist.

Step 5

Point npm, pip and docker at it

Each developer or pipeline makes a token under Tokens. It is shown once, with these commands already filled in for your address.

The replays here are npm, pip and docker. NuGet, Maven, RubyGems, Composer, CocoaPods, Swift, RPM and APT are switched on under Settings, Package types, and each has its own setup page: dotnet, Maven, Gradle, Bundler, Composer, CocoaPods, SwiftPM, dnf and yum and apt.

npm, pnpm, Yarn and Bun

One .npmrc next to package.json, so it travels with the repository:

registry=https://packages.example.com/
//packages.example.com/:_authToken=${NPM_TOKEN}

Lockfiles record your box as the source, so npm ci goes through it too. npm, pnpm, Yarn.

dana@laptop: ~/storefront
npm, allowed and blocked 0:00 / 0:00

Replay of real npm commands against a ForgeRepo™.

dana@laptop: ~/api
pip, allowed and blocked 0:00 / 0:00

Replay of real pip commands against a ForgeRepo™ with PyPI switched on.

pip, uv and Poetry

Switch on PyPI under Settings, Registries and add https://pypi.org. Then:

pip config set global.index-url \
  https://__token__:${REPO_TOKEN}@packages.example.com/pypi/simple/

A blocked project is simply not listed, so pip says no matching distribution, and the portal says why. pip, uv, Poetry.

docker, podman and Kubernetes

Switch on container images and add Docker Hub as https://registry-1.docker.io. Then log in with the token as the password and pull through your address:

echo "$REPO_TOKEN" | docker login packages.example.com -u dana --password-stdin
docker pull packages.example.com/nginx:latest

docker, Dockerfiles, compose, Kubernetes.

dana@laptop: ~
docker, allowed and blocked 0:00 / 0:00

Replay of real docker commands against a ForgeRepo™ with images switched on.

In CI

A token per pipeline, placed in its application and environment, stored as a secret. For GitHub Actions:

- uses: actions/setup-node@v4
  with:
    node-version: 22
    registry-url: https://packages.example.com/
- run: npm ci
  env:
    NODE_AUTH_TOKEN: ${{ secrets.REPO_TOKEN }}

GitHub Actions, GitLab CI, Jenkins, Azure Pipelines and others.

Step 6

Turn on the extras

ClamAV

Add COMPOSE_PROFILES=clamav to .env, run docker compose up -d, wait for it to be healthy, then add clamav under Settings, Malware.

Malware scanning

Cooling off

Settings, Policy, Cooling off, in hours. 72 is a sensible start. Exempt your own scopes.

Cooling off

Safe versions

Settings, Policy, Safe version resolution, from HIGH. Ranges step around vulnerable versions.

Safe version resolution

Single sign on

Settings, SSO. Register a web app at Okta, Entra ID, Google or Keycloak with the redirect the page shows, and press Check the provider.

Single sign on

Email

SMTP or Microsoft 365 through Graph. Digests, never a mail per event. Send a test before you switch it on.

Name, branding and email

Require tokens

Once every pipeline has one, tick Require tokens, and every download is tied to a person or a pipeline.

Require tokens

Upgrading

cd /data/docker/forgerepo      # /data/docker/npm-repo on an install from before the rename
sudo ./setup.sh --upgrade

That pulls the latest code, tags the running image as npm-repo:previous, rebuilds, restarts, and waits for the health check. It refuses to run over local edits, and never touches .env or your data. The schema updates itself on start. A bad upgrade goes back with:

docker tag npm-repo:previous npm-repo:latest && docker compose up -d

Plain docker compose up -d is not an upgrade

Compose builds this image rather than pulling one, and up -d reuses the image it already has. Use --upgrade, or docker compose up -d --build.

More in Upgrades, health and high availability and Backups, import and export.

Stuck on a step?

The documentation has 69 topics, written for developers and for admins, with the same screenshots you will see. And the questions page covers what people usually ask.