Skip to main content

Installation

SSH Portfolio can be installed via Docker or built from source with Go. This guide covers all installation methods and platform-specific instructions.

Prerequisites

Recommended for production deployments
  • Docker Engine 20.10+
  • Docker Compose 2.0+ (optional, for easier deployment)
  • 50MB disk space for the image
Check your installation:
docker --version
docker-compose --version

Installation Methods

Docker provides the easiest deployment with no Go toolchain required.
1

Clone the repository

git clone https://github.com/ab-70/ssh-portfolio.git
cd ssh-portfolio
2

Configure your portfolio

Edit config.yaml with your information:
nano config.yaml  # or use your preferred editor
See the Configuration Guide for detailed options.
3

Build and run with Docker Compose

docker-compose up -d
This uses the provided docker-compose.yml which:
  • Builds from the Dockerfile
  • Maps port 22 to the host
  • Creates a persistent volume for SSH keys
  • Restarts automatically on failure
4

Verify it's running

docker ps
You should see ssh-portfolio in the list. Check logs:
docker-compose logs -f
Expected output:
Starting SSH portfolio server on 0.0.0.0:22
Connect with: ssh localhost -p 22

Docker Compose Configuration

The default docker-compose.yml (lines 1-14):
docker-compose.yml
services:
  ssh-portfolio:
    build: .
    ports:
      - "22:22"
    environment:
      - SSH_PORT=22
    volumes:
      - ssh-keys:/app/.ssh
    restart: unless-stopped

volumes:
  ssh-keys:
If port 22 is already in use (e.g., by system SSH), change the port mapping:
ports:
  - "2222:22"  # Maps host port 2222 to container port 22

Method 2: Docker (Manual Build)

For more control over the Docker build process:
1

Clone and configure

git clone https://github.com/ab-70/ssh-portfolio.git
cd ssh-portfolio
# Edit config.yaml
2

Build the Docker image

docker build -t ssh-portfolio .
The Dockerfile uses a multi-stage build (Dockerfile:1-14):
  1. Builder stage: golang:1.24-alpine with Go compilation
  2. Runtime stage: Minimal alpine:3.21 with only the binary
3

Run the container

docker run -d \
  --name ssh-portfolio \
  -p 2222:22 \
  -e SSH_PORT=22 \
  -v ssh-keys:/app/.ssh \
  --restart unless-stopped \
  ssh-portfolio
4

Verify and test

docker logs ssh-portfolio
ssh localhost -p 2222

Method 3: Build from Source with Go

For development or when Docker isn’t available:
1

Clone the repository

git clone https://github.com/ab-70/ssh-portfolio.git
cd ssh-portfolio
2

Download dependencies

go mod download
This installs:
  • github.com/charmbracelet/bubbletea v1.3.10 - TUI framework
  • github.com/charmbracelet/lipgloss v1.1.0 - Terminal styling
  • github.com/charmbracelet/ssh - SSH server
  • github.com/charmbracelet/wish v1.4.7 - SSH middleware
  • gopkg.in/yaml.v3 v3.0.1 - YAML parsing
See go.mod (lines 5-11) for the complete list.
3

Edit config.yaml

cp config.yaml config.yaml.backup
nano config.yaml
4

Build the binary

go build -o ssh-portfolio .
This creates a single binary named ssh-portfolio in the current directory.
Optional: Build for different platforms:
# Linux AMD64
GOOS=linux GOARCH=amd64 go build -o ssh-portfolio-linux-amd64

# Linux ARM64 (for Raspberry Pi)
GOOS=linux GOARCH=arm64 go build -o ssh-portfolio-linux-arm64

# macOS
GOOS=darwin GOARCH=arm64 go build -o ssh-portfolio-macos
5

Run the server

./ssh-portfolio
Or with a custom port:
SSH_PORT=3000 ./ssh-portfolio
Expected output (main.go:52-53):
Starting SSH portfolio server on 0.0.0.0:2222
Connect with: ssh localhost -p 2222

Method 4: Install via go install (Coming Soon)

Once published to a Go module repository:
go install github.com/ab-70/ssh-portfolio@latest
This method isn’t available yet. Use Method 3 (build from source) instead.

Post-Installation Setup

SSH Host Keys

SSH Portfolio automatically generates SSH host keys on first run:
.ssh/id_ed25519      # Private key
.ssh/id_ed25519.pub  # Public key
The keys are generated at the path specified in main.go:38:
wish.WithHostKeyPath(".ssh/id_ed25519")
Protect your private key!
  • Never commit .ssh/ to version control (already in .gitignore)
  • Keep backups of your keys for consistent host identity
  • Use the same keys when redeploying to avoid host key verification warnings

Port Configuration

The server listens on port 2222 by default (main.go:25-29):
host := "0.0.0.0"
port := "2222"
if p := os.Getenv("SSH_PORT"); p != "" {
    port = p
}
Change the port by setting the SSH_PORT environment variable:
# In docker-compose.yml:
environment:
  - SSH_PORT=3000
ports:
  - "3000:3000"

Systemd Service (Linux)

For production Linux servers, create a systemd service:
1

Create service file

sudo nano /etc/systemd/system/ssh-portfolio.service
/etc/systemd/system/ssh-portfolio.service
[Unit]
Description=SSH Portfolio Server
After=network.target

[Service]
Type=simple
User=portfolio
WorkingDirectory=/opt/ssh-portfolio
Environment="SSH_PORT=2222"
ExecStart=/opt/ssh-portfolio/ssh-portfolio
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target
2

Create user and directories

sudo useradd -r -s /bin/false portfolio
sudo mkdir -p /opt/ssh-portfolio
sudo cp ssh-portfolio config.yaml /opt/ssh-portfolio/
sudo chown -R portfolio:portfolio /opt/ssh-portfolio
3

Enable and start

sudo systemctl daemon-reload
sudo systemctl enable ssh-portfolio
sudo systemctl start ssh-portfolio
4

Check status

sudo systemctl status ssh-portfolio
sudo journalctl -u ssh-portfolio -f

Verification

Test your installation:
1

Check server is running

docker ps | grep ssh-portfolio
docker logs ssh-portfolio
2

Test local connection

ssh localhost -p 2222
You should see your portfolio’s home screen.
3

Test remote connection (if applicable)

ssh your-server.com -p 2222
Ensure your firewall allows incoming connections on the SSH portfolio port.

Troubleshooting

Cause: config.yaml not found or invalid YAML syntaxSolutions:
  1. Check the file exists: ls -l config.yaml
  2. Verify YAML syntax: yamllint config.yaml
  3. Check the config path in code (config/config.go:63)
  4. Ensure file is readable: chmod 644 config.yaml
Cause: Another process is using the portSolutions:
  1. Check what’s using the port: lsof -i :2222 or netstat -tlnp | grep 2222
  2. Change the port: SSH_PORT=3000 ./ssh-portfolio
  3. Stop the conflicting service
Cause: SSH keys directory has wrong permissionsSolutions:
docker-compose down
docker volume rm ssh-portfolio_ssh-keys
docker-compose up -d
Cause: Go version is too oldSolution: Upgrade Go to 1.24.3+:
# Linux/macOS
wget https://go.dev/dl/go1.24.3.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.24.3.linux-amd64.tar.gz
go version
Cause: Firewall blocking the portSolutions:
  1. Check firewall: sudo ufw status or sudo firewall-cmd --list-all
  2. Open the port:
    # UFW (Ubuntu/Debian)
    sudo ufw allow 2222/tcp
    
    # firewalld (CentOS/RHEL)
    sudo firewall-cmd --permanent --add-port=2222/tcp
    sudo firewall-cmd --reload
    
    # iptables
    sudo iptables -A INPUT -p tcp --dport 2222 -j ACCEPT
    
  3. Check cloud provider security groups (AWS, GCP, Azure)
Cause: Host key changed (e.g., regenerated keys or connecting to a different server)Solution: Remove the old key:
ssh-keygen -R "[localhost]:2222"
Or for remote hosts:
ssh-keygen -R "[your-server.com]:2222"

Upgrading

Docker

cd ssh-portfolio
git pull
docker-compose down
docker-compose build
docker-compose up -d

Go/Binary

cd ssh-portfolio
git pull
go build -o ssh-portfolio .
sudo systemctl restart ssh-portfolio  # If using systemd
Always backup your config.yaml before upgrading:
cp config.yaml config.yaml.backup

Next Steps

Configuration

Learn how to customize your portfolio with config.yaml, ASCII art, and themes

Deployment

Deploy to production with nginx, custom domains, and monitoring

Build docs developers (and LLMs) love