Choose how to install
Option 1: GitHub (build from source)
Builds the images on your machine from the latest source code. Needs Git. Takes several minutes the first time. Recommended.
Option 2: Docker Hub (prebuilt images)
Pulls ready-made images, so nothing is built and no Git is needed. Faster, but the published images are older than the source. Use it for a trial.
Both options end at the same place: PMIS running at http://localhost:3000, and the steps in Sign in and set up.
Option 1: GitHub (build from source)
Follow these steps in order. Every command is for a terminal opened in the folder where you want PMIS to live.
1. Check the prerequisites
- Docker with Compose v2. Docker Desktop on Windows or macOS; Docker Engine with the Compose plugin on Linux. Check with
docker compose version. - Git, to download the source.
- OpenSSL, to generate the secrets. It is included on Linux and macOS, and in Git Bash on Windows. Windows PowerShell users can use the PowerShell block in step 3 instead.
- A free port 3000 for the web interface, or another port you set in step 3b.
- Internet access, to download the npm packages used by the build.
2. Clone the repository
git clone https://github.com/sneakywarwolf/PMIS.git
cd PMIS
Run every remaining command of this option from the PMIS folder.
3. Create the .env file and set the secrets
Copy the template. Linux, macOS and Git Bash:
cp .env.example .env
Windows Command Prompt:
copy .env.example .env
3a. Fill in the four required secrets. Each one must be its own long random value. Linux, macOS and Git Bash:
for k in POSTGRES_PASSWORD MINIO_ROOT_PASSWORD JWT_ACCESS_SECRET JWT_REFRESH_SECRET; do
sed -i.bak "s|^$k=CHANGE_ME|$k=$(openssl rand -hex 32)|" .env
done
rm -f .env.bak
grep '=CHANGE_ME' .env || echo "All secrets set"
Windows PowerShell, run inside the PMIS folder:
$names = 'POSTGRES_PASSWORD', 'MINIO_ROOT_PASSWORD', 'JWT_ACCESS_SECRET', 'JWT_REFRESH_SECRET'
$lines = Get-Content .env
foreach ($name in $names) {
$bytes = New-Object byte[] 32
[Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
$hex = -join ($bytes | ForEach-Object { $_.ToString('x2') })
$lines = $lines -replace "^$name=CHANGE_ME$", "$name=$hex"
}
[IO.File]::WriteAllLines((Resolve-Path .env).Path, $lines)
if (Select-String -Path .env -Pattern '=CHANGE_ME') { 'Some secrets are still CHANGE_ME' } else { 'All secrets set' }
.env private and never commit it. If you write a value by hand and it contains a $, write it as $$. The generated hex values never contain one.MINIO_ROOT_PASSWORD is the secret key of the object storage service (RustFS). The variable keeps its earlier name so existing .env files keep working.
3b. Optional settings (edit .env):
| Setting | When to change it |
|---|---|
ADMIN_PASSWORD | Set it to choose the first admin password yourself. If empty, a random password is generated and printed once (see Sign in and set up). |
FRONTEND_PORT, FRONTEND_URL | If port 3000 is taken, or users will reach PMIS at another address. FRONTEND_URL must be exactly the address typed in the browser. |
COOKIE_SECURE | Leave false for http://localhost. Set true only when PMIS is served over HTTPS (see Production). |
SEED_DEMO | Leave false. true creates demo accounts with public passwords. Use it only on a private test machine. |
4. Build and start PMIS
docker compose up -d --build
The first build downloads the npm packages and takes several minutes. Later starts are quick.
5. Wait until everything is healthy
docker compose ps
Wait until backend, frontend, postgres and rustfs show (healthy), and redis shows Up. The backend creates the database tables and loads the vulnerability library before it reports healthy.
Option 1 is finished. Continue with Sign in and set up.
Option 2: Docker Hub (prebuilt images)
Pulls the published images sneakywarwolf/pmis-backend and sneakywarwolf/pmis-frontend. Nothing is built and no source code or Git is needed. You still need two small files from the repository.
latest images were last pushed on 2026-10-08. They predate later fixes in the source, including the dependency updates and the move to Node 22. Use this option for a trial. For real use, choose Option 1, until newer images are published.1. Check the prerequisites
- Docker with Compose v2. Docker Desktop on Windows or macOS; Docker Engine with the Compose plugin on Linux. Check with
docker compose version. - OpenSSL, to generate the secrets. It is included on Linux and macOS, and in Git Bash on Windows. Windows PowerShell users can use the PowerShell block in step 3.
- A free port 3000 for the web interface, or another port you set in step 3b.
- Internet access, to pull the images.
2. Download the two files
Linux, macOS and Git Bash:
mkdir pmis && cd pmis
curl -fsSLO https://raw.githubusercontent.com/sneakywarwolf/PMIS/master/docker-compose.yml
curl -fsSL https://raw.githubusercontent.com/sneakywarwolf/PMIS/master/.env.example -o .env
Windows PowerShell (note curl.exe, which PowerShell does not alias):
mkdir pmis; cd pmis
curl.exe -fsSLO https://raw.githubusercontent.com/sneakywarwolf/PMIS/master/docker-compose.yml
curl.exe -fsSL https://raw.githubusercontent.com/sneakywarwolf/PMIS/master/.env.example -o .env
Run every remaining command of this option from the pmis folder.
3. Set the secrets in .env
3a. Fill in the four required secrets. Linux, macOS and Git Bash:
for k in POSTGRES_PASSWORD MINIO_ROOT_PASSWORD JWT_ACCESS_SECRET JWT_REFRESH_SECRET; do
sed -i.bak "s|^$k=CHANGE_ME|$k=$(openssl rand -hex 32)|" .env
done
rm -f .env.bak
grep '=CHANGE_ME' .env || echo "All secrets set"
Windows PowerShell, run inside the pmis folder:
$names = 'POSTGRES_PASSWORD', 'MINIO_ROOT_PASSWORD', 'JWT_ACCESS_SECRET', 'JWT_REFRESH_SECRET'
$lines = Get-Content .env
foreach ($name in $names) {
$bytes = New-Object byte[] 32
[Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
$hex = -join ($bytes | ForEach-Object { $_.ToString('x2') })
$lines = $lines -replace "^$name=CHANGE_ME$", "$name=$hex"
}
[IO.File]::WriteAllLines((Resolve-Path .env).Path, $lines)
if (Select-String -Path .env -Pattern '=CHANGE_ME') { 'Some secrets are still CHANGE_ME' } else { 'All secrets set' }
.env private and never share it. If you write a value by hand and it contains a $, write it as $$.MINIO_ROOT_PASSWORD is the secret key of the object storage service (RustFS). The variable keeps its earlier name so existing .env files keep working.
3b. Optional settings (edit .env): the same table as in Option 1, step 3b. Leave PMIS_VERSION=latest and PMIS_IMAGE_REPO=sneakywarwolf as they are, unless you host the images in your own registry.
4. Pull the images
Pull the two PMIS images, then the supporting PostgreSQL, Redis and RustFS (object storage) images that the stack uses:
docker pull sneakywarwolf/pmis-frontend:latest
docker pull sneakywarwolf/pmis-backend:latest
docker compose pull
The images are published for amd64 and arm64. If Docker reports HTTP 429 or "Too Many Requests", see the troubleshooting table below.
5. Start PMIS without building
docker compose up -d --no-build
--no-build makes Docker stop with an error if an image is missing, instead of building from source, which this folder cannot do.
6. Wait until everything is healthy
docker compose ps
Wait until backend, frontend, postgres and rustfs show (healthy), and redis shows Up.
Option 2 is finished. Continue with Sign in and set up.
Sign in and set up
These steps are the same for both options.
Get the first admin password
If you did not set ADMIN_PASSWORD, a random password was generated and printed once, when the admin account was created. Linux, macOS and Git Bash:
docker compose logs backend | grep "First login"
Windows PowerShell:
docker compose logs backend | Select-String "First login"
The line appears only on the start that created the admin account. If it is missing, see Troubleshooting. Store the password safely.
Sign in
- Open
http://localhost:3000, or yourFRONTEND_URL. - Sign in with username
adminand that password. - Set a new password when prompted. The default policy needs at least 14 characters, including an uppercase letter, a number and a symbol.
Set up the application
- Your profile: open Profile and fill in your full name, phone and designation. Reports use these details.
- Users: open Users and create AVPs, Team Leads and Testers. Assign each tester to a Team Lead and each Team Lead to a manager. To add many users at once, download the sample file on the same page and upload it as CSV or XLSX. New users must change their password at first sign-in.
- Report templates: open Reports, upload your templates and activate them. Sample Word templates for PCI and VAPT (Web, API, Mobile, Thick Client) are in the
Report Template/folder of the source. They are not loaded automatically. See Reports. - Vulnerability library: the library is preloaded on first start. Admins can extend it or replace it by bulk upload under Vulnerabilities.
- Settings: review Settings for session timeout, password policy, retention periods and branding.
Before other people use PMIS, complete the production checklist.
Troubleshooting
| Symptom | Cause and fix |
|---|---|
set POSTGRES_PASSWORD in .env (or another secret name) | .env is missing or a required value is empty. See step 3a of your option. |
backend keeps restarting | Run docker compose logs backend. The most common cause is a JWT secret still set to CHANGE_ME. |
rustfs never becomes healthy | Run docker compose logs rustfs. The usual cause is a data volume that the storage user cannot write to. Named volumes created by Docker work; for a bind mount, make the folder writable by user ID 10001. |
| Port 3000 already in use | Set FRONTEND_PORT (for example 3001) and the matching FRONTEND_URL, then run docker compose up -d (Option 1) or docker compose up -d --no-build (Option 2). |
No First login line in the log | The admin account already existed, or ADMIN_PASSWORD was set. Use that password. |
Sign-in works on HTTPS but not on http:// | COOKIE_SECURE=true requires HTTPS. Set it to false for plain HTTP on localhost. |
| "Account is temporarily locked" | Too many failed sign-ins. Wait 15 minutes and try again. |
| A user forgot their password | An Admin resets it under Users. The user then sets a new one at next sign-in. |
| HTTP 429 or "Too Many Requests" when pulling images (Option 2) | Docker Hub limits anonymous pulls. Run docker login with a free Docker Hub account, then retry the pull. |
no matching manifest for linux/... (Option 2) | The PMIS images are published for linux/amd64 and linux/arm64 only. On another CPU type, use Option 1. |
No such image when using --no-build (Option 2) | An image was not pulled. Run the docker pull commands in step 4, or use Option 1. |
curl cannot reach raw.githubusercontent.com (Option 2) | Your network blocks downloads from GitHub. Download docker-compose.yml and .env.example in a browser, or use Option 1. |
| Build fails while downloading packages (Option 1) | Docker builds need internet access to the npm registry and to cdn.sheetjs.com. Behind a corporate proxy, configure Docker's proxy settings for builds. |
Build stops with npm error Exit handler never called (Option 1) | npm lost its connection during the build. Run docker compose up -d --build again. Behind a proxy, configure Docker's build proxy first. |
Stopping and removing
docker compose stop # stop, keep everything
docker compose start # start again
docker compose down # remove containers, keep data volumes
docker compose down -v also deletes the data volumes, including the database and all uploaded files. This cannot be undone. Take a backup first.Development setup (contributors)
For changing PMIS itself, not for running it for a team. The database, cache and storage run in Docker. The API and web interface run on the host with npm.
- Prerequisites: Docker, Git and Node.js 22 (the version the production images use).
- First-time setup from the PMIS folder:
bash setup.shon Linux or macOS, orsetup.baton Windows. The scripts start the infrastructure fromdocker-compose.dev.yml, install dependencies, create the database and start both apps. The admin password is printed by the seed step in the same terminal. - Windows helpers:
start-dev.bat,start.bat,stop.bat, andreset-db.batto reset development data. - Web interface at
http://localhost:3000, API athttp://localhost:4000.