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.
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-repo | install somewhere else |
--port 4444 | host port to publish on, always on 127.0.0.1 |
--name npm-repo | container name, to run two on one box |
--no-start | set it all up but do not start it |
--db-host … | join a shared MariaDB or RDS as a second node |
--upgrade | pull, 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"
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.
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.
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.
Replay of real npm commands against a ForgeRepo™.
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
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.
Cooling off
Settings, Policy, Cooling off, in hours. 72 is a sensible start. Exempt your own scopes.
Cooling offSafe versions
Settings, Policy, Safe version resolution, from HIGH. Ranges step around vulnerable versions.
Safe version resolutionSingle 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 onSMTP or Microsoft 365 through Graph. Digests, never a mail per event. Send a test before you switch it on.
Name, branding and emailRequire tokens
Once every pipeline has one, tick Require tokens, and every download is tied to a person or a pipeline.
Require tokensUpgrading
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.