Port Keeper Documentation
Port Keeper is a comprehensive tool for managing local development ports, preventing conflicts, and enabling team collaboration.
🔍 Smart Port Detection
Automatically detects which ports are in use and by which processes
📌 Port Reservation System
Reserve ports for specific projects to avoid conflicts
🤝 Team Collaboration
Export and share port configurations with your team
Why Port Keeper?
Managing ports in development environments can be challenging:
- Port conflicts when running multiple services
- Team members using the same ports for different projects
- Difficulty tracking which ports are used by which projects
- Manual process killing when ports are stuck
Port Keeper solves these problems with an intelligent reservation system and comprehensive management tools.
Installation
Requirements
- Node.js 14.0.0 or higher
- npm or yarn
- macOS, Linux, or Windows
Global Installation (Recommended)
npm install -g portkeeper
yarn global add portkeeper
pnpm add -g portkeeper
Install from Source
git clone https://github.com/ahmadzein/portkeeper.git
cd portkeeper
npm install
npm link
Verify Installation
portman --version
# Output: 1.0.0
portman --help
# Shows all available commands
sudo
for global installation.
Quick Start
Basic Workflow
-
Check if a port is available
portman check 3000
-
Reserve the port for your project
portman reserve 3000 -n "my-api" -d "API server"
-
Start your development server
npm run dev # Your server runs on port 3000
-
Release the port when done
portman release 3000
GUI Quick Start
# Launch the desktop application
portman gui
The GUI provides a visual interface for all port management tasks.
Core Concepts
Port States
🟢 Free
Port is available and not in use by any process
🟡 Reserved
Port is reserved for a project but not currently active
🔴 In Use
Port is actively being used by a running process
Reservation System
Port Keeper uses a SQLite database to track port reservations:
- Project Name: Identifies which project owns the port
- Description: Optional details about port usage
- Tags: Categorize ports (e.g., frontend, backend, database)
- Auto-release: Automatically release when process exits
Port Scanning
Port Keeper uses platform-specific commands to detect active ports:
- macOS/Linux: Uses
lsof
command - Windows: Uses
netstat
command
check Command
Check the status of a specific port.
Syntax
portman check <port> [options]
Arguments
Argument | Description | Required |
---|---|---|
<port> |
Port number to check (1-65535) | Yes |
Options
Option | Alias | Description | Default |
---|---|---|---|
--json |
- | Output in JSON format | false |
Examples
Check if port is free
portman check 3000
# Output: ✓ Port 3000 is free
Check reserved port
portman check 3000
# Output: ⚠ Port 3000 is reserved for "my-api"
# Description: API server
Check port in use
portman check 3000
# Output: ✗ Port 3000 is in use (PID: 12345, Process: node)
JSON output
portman check 3000 --json
# Output:
{
"port": 3000,
"status": "reserved",
"projectName": "my-api",
"description": "API server"
}
Exit Codes
0
- Port is free1
- Port is reserved or in use2
- Invalid port number
reserve Command
Reserve a port for a specific project.
Syntax
portman reserve <port> [options]
Arguments
Argument | Description | Required |
---|---|---|
<port> |
Port number to reserve (1-65535) | Yes |
Options
Option | Alias | Description | Default |
---|---|---|---|
--name <name> |
-n |
Project name | Required |
--description <desc> |
-d |
Port description | - |
--tags <tags> |
-t |
Comma-separated tags | - |
--auto-release |
-a |
Auto-release when process exits | false |
--json |
- | Output in JSON format | false |
Examples
Basic reservation
portman reserve 3000 -n "my-api"
# Output: ✓ Port 3000 reserved for "my-api"
With description
portman reserve 3000 -n "my-api" -d "Main API server"
# Output: ✓ Port 3000 reserved for "my-api"
# Description: Main API server
With tags
portman reserve 3000 -n "my-api" -t "backend,production"
# Output: ✓ Port 3000 reserved for "my-api"
# Tags: backend, production
Auto-release option
portman reserve 3000 -n "test-server" -a
# Output: ✓ Port 3000 reserved for "test-server" (auto-release enabled)
Error Handling
- Port already in use: Cannot reserve a port that's actively being used
- Port already reserved: Cannot reserve a port that's already reserved
- Invalid port: Port must be between 1 and 65535
release Command
Release one or more reserved ports.
Syntax
portman release <ports...> [options]
Arguments
Argument | Description | Required |
---|---|---|
<ports...> |
One or more port numbers to release | Yes |
Options
Option | Alias | Description | Default |
---|---|---|---|
--json |
- | Output in JSON format | false |
Examples
Release single port
portman release 3000
# Output: ✓ Port 3000 released
Release multiple ports
portman release 3000 3001 3002
# Output: ✓ Port 3000 released
# ✓ Port 3001 released
# ✓ Port 3002 released
#
# Summary: 3 released, 0 failed
Mixed results
portman release 3000 3001 3002
# Output: ✓ Port 3000 released
# ✗ Port 3001 not found or not reserved
# ✓ Port 3002 released
#
# Summary: 2 released, 1 failed
JSON output
portman release 3000 3001 --json
# Output:
{
"results": [
{"port": 3000, "status": "success"},
{"port": 3001, "status": "error", "error": "Port not reserved"}
]
}
list Command
List all reserved and in-use ports.
Syntax
portman list [options]
Options
Option | Alias | Description | Default |
---|---|---|---|
--status <status> |
-s |
Filter by status (reserved/in-use) | all |
--project <name> |
-p |
Filter by project name (partial match) | - |
--tags <tags> |
-t |
Filter by tags | - |
--json |
- | Output in JSON format | false |
Examples
List all ports
portman list
# Output:
Port Project Status Description Reserved At
──────────────────────────────────────────────────────────────────────
3000 my-api in-use API server 26/07/2025, 10:00:00
3001 frontend reserved React dev server 26/07/2025, 09:30:00
8080 admin-ui reserved Admin dashboard 25/07/2025, 14:20:00
Filter by status
portman list -s reserved
# Shows only reserved ports
Filter by project
portman list -p api
# Shows ports for projects containing "api"
Filter by tags
portman list -t backend
# Shows ports tagged with "backend"
scan Command
Scan for all active ports on the system.
Syntax
portman scan [options]
Options
Option | Alias | Description | Default |
---|---|---|---|
--json |
- | Output in JSON format | false |
Examples
Basic scan
portman scan
# Output:
Scanning for active ports...
Found 15 active port(s):
Port PID Process Project
────────────────────────────────────
3000 12345 node my-api
3001 12346 node -
5432 1234 postgres -
6379 2345 redis-server -
8080 3456 java -
JSON output for automation
portman scan --json
# Output:
[
{
"number": 3000,
"pid": 12345,
"processName": "node",
"state": "LISTEN",
"address": "127.0.0.1",
"projectName": "my-api",
"description": "API server"
},
{
"number": 3001,
"pid": 12346,
"processName": "node",
"state": "LISTEN"
}
]
Platform Differences
- macOS/Linux: Uses
lsof -i -P -n | grep LISTEN
- Windows: Uses
netstat -ano | findstr LISTENING
kill Command
Kill the process using a specific port.
Syntax
portman kill <ports...> [options]
Arguments
Argument | Description | Required |
---|---|---|
<ports...> |
One or more port numbers | Yes |
Options
Option | Alias | Description | Default |
---|---|---|---|
--force |
-f |
Skip confirmation prompt | false |
--json |
- | Output in JSON format | false |
Examples
Kill single process
portman kill 3000
# Output:
Killing process on port 3000...
✓ Process on port 3000 killed (PID: 12345)
Kill multiple processes
portman kill 3000 3001 3002
# Output:
✓ Process on port 3000 killed
✓ Process on port 3001 killed
✗ No process found on port 3002
Summary: 2 killed, 1 failed
Force kill without confirmation
portman kill 3000 -f
# Kills immediately without asking
request Command
Request and reserve multiple available ports.
Syntax
portman request <count> [options]
Arguments
Argument | Description | Required |
---|---|---|
<count> |
Number of ports to request | Yes |
Options
Option | Alias | Description | Default |
---|---|---|---|
--name <name> |
-n |
Project name | Required |
--description <desc> |
-d |
Description for the ports | - |
--tags <tags> |
-t |
Comma-separated tags | - |
--sequential |
-s |
Find sequential ports | true |
--random |
-r |
Find random ports | false |
--start <port> |
- | Start port for search | 3000 |
--end <port> |
- | End port for search | 9999 |
--avoid <ports> |
- | Comma-separated ports to avoid | - |
--json |
- | Output in JSON format | false |
Examples
Request sequential ports
portman request 3 -n "microservices"
# Output:
🔍 Searching for 3 available port(s)...
✓ Successfully reserved 3 port(s) for "microservices": 3000, 3001, 3002
Reserved Ports:
• Port 3000: microservices-1
• Port 3001: microservices-2
• Port 3002: microservices-3
Request random ports
portman request 5 -n "test-suite" -r
# Finds 5 random available ports
Custom port range
portman request 2 -n "api" --start 8000 --end 8999
# Searches only between ports 8000-8999
Avoid specific ports
portman request 3 -n "app" --avoid "3000,3001,8080"
# Will not use ports 3000, 3001, or 8080
export Command
Export port configuration to JSON.
Syntax
portman export [options]
Options
Option | Alias | Description | Default |
---|---|---|---|
--output <file> |
-o |
Output to file instead of stdout | - |
--pretty |
-p |
Pretty print JSON | false |
Examples
Export to stdout
portman export
# Outputs JSON to console
Export to file
portman export -o ports-backup.json
# Output: ✓ Exported 12 ports to ports-backup.json
Pretty print
portman export -p
# Output:
{
"version": "1.0.0",
"exportDate": "2025-07-26T10:00:00.000Z",
"ports": [
{
"number": 3000,
"projectName": "my-api",
"description": "API server",
"tags": ["backend"],
"reservedAt": "2025-07-26T09:00:00.000Z"
}
]
}
import Command
Import port configuration from JSON.
Syntax
portman import [file] [options]
Arguments
Argument | Description | Required |
---|---|---|
[file] |
JSON file to import (or stdin if omitted) | No |
Options
Option | Alias | Description | Default |
---|---|---|---|
--force |
-f |
Skip already reserved ports | false |
--json |
- | Output in JSON format | false |
Examples
Import from file
portman import ports-backup.json
# Output:
Importing from export version 1.0.0
Export date: 26/07/2025, 10:00:00
Ports to import: 12
✓ Imported port 3000 for my-api
✓ Imported port 3001 for frontend
⚠ Skipping port 3002 (already reserved)
✗ Cannot import port 8080 (currently in use)
Import complete: 10 imported, 1 skipped, 1 error
Import from stdin
cat backup.json | portman import
Force import
portman import backup.json -f
# Skips conflicting ports without errors
gui Command
Launch the Port Keeper desktop application.
Syntax
portman gui
Description
Launches the Electron-based GUI application that provides a visual interface for all port management tasks.
Features
- Visual port dashboard
- Real-time port scanning
- Drag-and-drop import
- Dark/Light theme support
- Keyboard shortcuts
Example
portman gui
# Output: 🚀 Launching Port Keeper GUI...
ai Command
Display AI agent instructions and integration guide.
Syntax
portman ai [options]
Options
Option | Alias | Description |
---|---|---|
--json |
- | Output in JSON format for machine processing |
Description
The ai
command provides comprehensive instructions for AI agents and automation tools to interact with Port Keeper. It supports two output modes:
- Human-readable: Formatted guide with examples and best practices
- JSON: Complete command schema for programmatic access
Examples
Display human-readable guide
portman ai
# Output:
# 🤖 Port Keeper - AI Agent Integration Guide
#
# PURPOSE:
# Port Keeper helps manage local development ports...
#
# KEY CAPABILITIES:
# • Check port availability and status
# • Reserve ports for specific projects
# ...
Get JSON schema for automation
portman ai --json
# Output:
{
"name": "Port Keeper",
"version": "1.0.0",
"description": "A comprehensive tool for managing local development ports",
"capabilities": [
"Check port status (free/reserved/in-use)",
"Reserve ports for projects",
...
],
"commands": {
"check": {
"syntax": "portman check [--json]",
"description": "Check if a port is free, reserved, or in use",
...
}
}
}
Use Cases
- AI coding assistants learning Port Keeper commands
- Generating automation scripts
- Building integrations with Port Keeper
- Creating documentation for team members
portman ai --json | jq '.commands'
to explore specific command schemas.
GUI Dashboard
The main dashboard provides an overview of all reserved ports.
Features
- Table view of all reserved ports
- Search and filter capabilities
- Quick actions: Check, Release, Kill
- Sort by any column
- Bulk selection and operations
Table Columns
- Port: Port number
- Project: Project name
- Status: Reserved or In Use
- PID: Process ID (if active)
- Description: Port description
- Reserved At: Reservation timestamp
- Actions: Quick action buttons
Active Ports View
Real-time view of all active ports on the system.
Features
- Live scanning of active ports
- Shows process information
- Highlights managed vs unmanaged ports
- Kill processes directly
- Reserve unmanaged ports
Port Information
- Green: Managed by Port Keeper
- Yellow: System or third-party process
- Red: Potentially conflicting port
Keyboard Shortcuts
Keyboard shortcuts for efficient port management.
Global Shortcuts
Shortcut | Action |
---|---|
Cmd/Ctrl + N | New Reservation |
Cmd/Ctrl + R | Refresh List |
Cmd/Ctrl + F | Focus Search |
Cmd/Ctrl + S | Scan Active Ports |
Cmd/Ctrl + E | Export Configuration |
Cmd/Ctrl + I | Import Configuration |
Cmd/Ctrl + , | Open Settings |
Table Shortcuts
Shortcut | Action |
---|---|
Space | Select/Deselect Row |
Delete | Release Selected Ports |
Enter | View Port Details |
Automation & Scripting
Port Keeper is designed for automation with comprehensive JSON support.
Shell Scripts
Port allocation script
#!/bin/bash
# allocate-ports.sh
PROJECT="my-app"
PORTS_NEEDED=3
# Request ports
RESULT=$(portman request $PORTS_NEEDED -n "$PROJECT" --json)
PORTS=$(echo $RESULT | jq -r '.ports[].number' | tr '\n' ' ')
# Export as environment variables
export API_PORT=$(echo $PORTS | awk '{print $1}')
export WEB_PORT=$(echo $PORTS | awk '{print $2}')
export ADMIN_PORT=$(echo $PORTS | awk '{print $3}')
echo "Allocated ports: API=$API_PORT, WEB=$WEB_PORT, ADMIN=$ADMIN_PORT"
# Start services
npm run start:api -- --port $API_PORT &
npm run start:web -- --port $WEB_PORT &
npm run start:admin -- --port $ADMIN_PORT &
# Cleanup on exit
trap "portman release $PORTS" EXIT
wait
Docker Integration
Dynamic port allocation for containers
#!/bin/bash
# docker-ports.sh
# Request port for container
PORT_JSON=$(portman request 1 -n "docker-$1" --json)
PORT=$(echo $PORT_JSON | jq -r '.ports[0].number')
# Run container with allocated port
docker run -d \
--name "$1" \
-p "$PORT:80" \
-e "EXTERNAL_PORT=$PORT" \
"$2"
echo "Container $1 running on port $PORT"
CI/CD Integration
GitHub Actions
name: Integration Tests
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Port Keeper
run: npm install -g portkeeper
- name: Allocate Test Ports
id: ports
run: |
PORTS=$(portman request 3 -n "ci-${{ github.run_id }}" --json)
echo "::set-output name=ports::$PORTS"
echo "API_PORT=$(echo $PORTS | jq -r '.ports[0].number')" >> $GITHUB_ENV
echo "DB_PORT=$(echo $PORTS | jq -r '.ports[1].number')" >> $GITHUB_ENV
echo "REDIS_PORT=$(echo $PORTS | jq -r '.ports[2].number')" >> $GITHUB_ENV
- name: Run Tests
run: |
npm test
- name: Cleanup Ports
if: always()
run: |
NUMBERS=$(echo '${{ steps.ports.outputs.ports }}' | jq -r '.ports[].number' | tr '\n' ' ')
portman release $NUMBERS
AI Agent Integration
Port Keeper provides JSON output for seamless AI agent integration.
Python Example
import subprocess
import json
import sys
class PortManager:
def __init__(self):
self.command_base = ['portman']
def execute(self, args):
"""Execute portman command and return JSON result"""
cmd = self.command_base + args + ['--json']
try:
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
return json.loads(result.stdout)
except subprocess.CalledProcessError as e:
return {'error': e.stderr, 'code': e.returncode}
def check_port(self, port):
"""Check if a port is available"""
return self.execute(['check', str(port)])
def reserve_port(self, port, project, description=None, tags=None):
"""Reserve a port for a project"""
args = ['reserve', str(port), '-n', project]
if description:
args.extend(['-d', description])
if tags:
args.extend(['-t', ','.join(tags)])
return self.execute(args)
def request_ports(self, count, project, **kwargs):
"""Request multiple ports"""
args = ['request', str(count), '-n', project]
for key, value in kwargs.items():
if value is not None:
args.extend([f'--{key}', str(value)])
return self.execute(args)
def list_ports(self, status=None, project=None):
"""List ports with optional filters"""
args = ['list']
if status:
args.extend(['-s', status])
if project:
args.extend(['-p', project])
return self.execute(args)
# Usage example
pm = PortManager()
# Check if port is free
status = pm.check_port(3000)
if status.get('status') == 'free':
# Reserve it
result = pm.reserve_port(3000, 'my-api', 'API Server', ['backend', 'prod'])
print(f"Reserved: {result}")
# Request multiple ports
ports = pm.request_ports(5, 'microservices', sequential=True, start=4000)
print(f"Allocated ports: {[p['number'] for p in ports.get('ports', [])]}")
# List all reserved ports
all_ports = pm.list_ports(status='reserved')
for port in all_ports:
print(f"Port {port['number']}: {port['projectName']}")
Node.js Example
const { exec } = require('child_process');
const util = require('util');
const execPromise = util.promisify(exec);
class PortManager {
async execute(command) {
try {
const { stdout } = await execPromise(`portman ${command} --json`);
return JSON.parse(stdout);
} catch (error) {
throw new Error(`Command failed: ${error.message}`);
}
}
async checkPort(port) {
return this.execute(`check ${port}`);
}
async reservePort(port, project, options = {}) {
let cmd = `reserve ${port} -n "${project}"`;
if (options.description) cmd += ` -d "${options.description}"`;
if (options.tags) cmd += ` -t "${options.tags.join(',')}"`;
return this.execute(cmd);
}
async requestPorts(count, project, options = {}) {
let cmd = `request ${count} -n "${project}"`;
Object.entries(options).forEach(([key, value]) => {
if (value !== undefined) cmd += ` --${key} ${value}`;
});
return this.execute(cmd);
}
async listPorts(filters = {}) {
let cmd = 'list';
if (filters.status) cmd += ` -s ${filters.status}`;
if (filters.project) cmd += ` -p "${filters.project}"`;
return this.execute(cmd);
}
async releasePorts(...ports) {
return this.execute(`release ${ports.join(' ')}`);
}
}
// Usage
const pm = new PortManager();
async function allocatePorts() {
try {
// Check port availability
const status = await pm.checkPort(3000);
console.log('Port 3000 status:', status.status);
// Request multiple ports
const result = await pm.requestPorts(3, 'my-service', {
sequential: true,
start: 4000
});
const ports = result.ports.map(p => p.number);
console.log('Allocated ports:', ports);
// Do something with the ports...
// Release when done
await pm.releasePorts(...ports);
console.log('Ports released');
} catch (error) {
console.error('Error:', error.message);
}
}
allocatePorts();
JSON API Reference
Complete reference for JSON responses from all commands.
Response Structures
Port Object
interface Port {
number: number;
projectName: string;
description?: string;
status: "reserved" | "in-use";
pid?: number;
reservedAt: string; // ISO 8601
lastUsed?: string; // ISO 8601
autoRelease: boolean;
tags: string[];
}
Active Port Object
interface ActivePort {
number: number;
pid: number;
processName?: string;
state?: string;
address?: string;
// If managed by Port Keeper:
projectName?: string;
description?: string;
tags?: string[];
reservedAt?: string;
}
Check Response
interface CheckResponse {
port: number;
status: "free" | "reserved" | "in-use";
// If reserved:
projectName?: string;
description?: string;
// If in use:
pid?: number;
processName?: string;
}
Request Response
interface RequestResponse {
ports: Port[];
summary: string;
}
Operation Result
interface OperationResult {
port: number;
status: "success" | "error";
error?: string;
pid?: number; // For kill command
}
Import Response
interface ImportResponse {
imported: number;
skipped: number;
errors: number;
total: number;
}
Error Responses
When an error occurs, the process exits with a non-zero code and outputs error to stderr:
{
"error": "Port 3000 is already in use",
"code": "PORT_IN_USE",
"details": {
"port": 3000,
"pid": 12345,
"process": "node"
}
}
Troubleshooting
Common issues and their solutions.
Permission Errors
Problem
Error: EACCES: permission denied
Solution
- Use ports above 1024 (no special permissions required)
- Run with sudo for system ports (not recommended)
- Configure your system to allow non-root port binding
Module Version Mismatch
Problem
Error: NODE_MODULE_VERSION mismatch
Solution
# For CLI usage
npm run fix:node
# For GUI usage
npm run fix:electron
Port Already in Use
Problem
Cannot reserve port - already in use
Solution
- Check what's using the port:
portman check 3000
- Kill the process if safe:
portman kill 3000
- Or choose a different port:
portman request 1 -n "my-app"
Database Errors
Problem
Database is locked or corrupted
Solution
- Database location:
~/.portkeeper/ports.db
- Back up and remove if corrupted
- Port Keeper will create a new database
GUI Won't Launch
Problem
GUI fails to start or shows blank window
Solution
- Ensure GUI is built:
npm run build:gui
- Check for Electron errors in console
- Try running in development mode:
npm run dev:gui
- Verify display server on Linux