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
Note: On macOS/Linux, you might need to use sudo for global installation.

Quick Start

Basic Workflow

  1. Check if a port is available

    portman check 3000
  2. Reserve the port for your project

    portman reserve 3000 -n "my-api" -d "API server"
  3. Start your development server

    npm run dev # Your server runs on port 3000
  4. 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 free
  • 1 - Port is reserved or in use
  • 2 - 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
Warning: Killing processes may cause data loss. Always save your work before killing a process.

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...
Note: The GUI requires a graphical environment. Use CLI commands for headless servers.

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
Tip: Use 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

  1. Check what's using the port: portman check 3000
  2. Kill the process if safe: portman kill 3000
  3. 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