Skip to content

Outline VPN Cheatsheet

Outline VPN is an open-source VPN solution developed by Jigsaw (a subsidiary of Alphabet Inc.) that prioritizes ease of use, security, and resistance to blocking. Built on the Shadowsocks protocol, Outline provides a simple way to create and manage personal VPN servers while offering strong encryption and obfuscation capabilities that make it difficult for network administrators to detect and block VPN traffic.

Platform Overview

Architecture and Design Philosophy

Outline VPN consists of two main components: the Outline Manager for server administration and the Outline Client for end-user connectivity. The system is designed with simplicity and security as core principles, making it accessible to users without extensive technical knowledge while maintaining enterprise-grade security standards.

The Outline Manager is a desktop application that allows administrators to deploy and manage Outline servers on various cloud platforms including DigitalOcean, Google Cloud Platform, Amazon Web Services, and Vultr. The manager handles server provisioning, user key generation, and traffic monitoring through an intuitive graphical interface.

The Outline Client applications are available for Windows, macOS, Linux, iOS, and Android platforms. These clients use the generated access keys to establish secure connections to Outline servers, providing encrypted tunneling for all network traffic.

Key Features

bash
# Core Outline VPN Capabilities
- Shadowsocks-based protocol for enhanced obfuscation
- Automatic server deployment on major cloud platforms
- Simple access key sharing system
- Cross-platform client applications
- Traffic monitoring and usage analytics
- Resistance to deep packet inspection (DPI)
- No logging policy and privacy-focused design
- Open-source transparency and auditability

Server Installation and Setup

Manual Server Installation

bash
# Install Outline server on Ubuntu/Debian
curl -sS https://raw.githubusercontent.com/Jigsaw-Code/outline-server/master/src/server_manager/install_scripts/install_server.sh | bash

# The installation script will:
# 1. Install Docker and Docker Compose
# 2. Download and run the Outline server container
# 3. Generate initial configuration
# 4. Display management API URL and certificate fingerprint

# Verify installation
sudo docker ps | grep outline

# Check server status
sudo docker logs outline-server

# View server configuration
sudo cat /opt/outline/access.txt

# Manual Docker installation (alternative method)
sudo docker run -d \
  --name outline-server \
  --restart unless-stopped \
  -p 443:443/tcp \
  -p 443:443/udp \
  -p 8080:8080/tcp \
  -v outline-data:/root/shadowbox/persisted-state \
  quay.io/outline/shadowbox:stable

# Configure firewall for Outline server
sudo ufw allow 443/tcp
sudo ufw allow 443/udp
sudo ufw allow 8080/tcp
sudo ufw enable

# Enable IP forwarding
echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf
echo 'net.ipv6.conf.all.forwarding=1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Cloud Platform Deployment

bash
# DigitalOcean deployment script
#!/bin/bash
DROPLET_NAME="outline-vpn-server"
REGION="nyc3"
SIZE="s-1vcpu-1gb"
IMAGE="ubuntu-20-04-x64"

# Create droplet
doctl compute droplet create $DROPLET_NAME \
  --region $REGION \
  --size $SIZE \
  --image $IMAGE \
  --ssh-keys $(doctl compute ssh-key list --format ID --no-header | tr '\n' ',') \
  --wait

# Get droplet IP
DROPLET_IP=$(doctl compute droplet list --format Name,PublicIPv4 --no-header | grep $DROPLET_NAME | awk '{print $2}')

# Install Outline server
ssh root@$DROPLET_IP 'curl -sS https://raw.githubusercontent.com/Jigsaw-Code/outline-server/master/src/server_manager/install_scripts/install_server.sh | bash'

# AWS EC2 deployment with Terraform
cat > outline-server.tf << 'EOF'
provider "aws" {
  region = "us-east-1"
}

resource "aws_instance" "outline_server" {
  ami           = "ami-0c02fb55956c7d316"  # Ubuntu 20.04 LTS
  instance_type = "t3.micro"
  key_name      = "your-key-pair"
  
  vpc_security_group_ids = [aws_security_group.outline_sg.id]
  
  user_data = <<-EOF
    #!/bin/bash
    curl -sS https://raw.githubusercontent.com/Jigsaw-Code/outline-server/master/src/server_manager/install_scripts/install_server.sh | bash
  EOF
  
  tags = {
    Name = "Outline VPN Server"
  }
}

resource "aws_security_group" "outline_sg" {
  name_prefix = "outline-server-"
  
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "udp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  ingress {
    from_port   = 8080
    to_port     = 8080
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

output "server_ip" {
  value = aws_instance.outline_server.public_ip
}
EOF

# Deploy with Terraform
terraform init
terraform plan
terraform apply

Docker Compose Setup

yaml
# docker-compose.yml for Outline server
version: '3.8'

services:
  outline-server:
    image: quay.io/outline/shadowbox:stable
    container_name: outline-server
    restart: unless-stopped
    ports:
      - "443:443/tcp"
      - "443:443/udp"
      - "8080:8080/tcp"
    volumes:
      - outline-data:/root/shadowbox/persisted-state
      - /etc/letsencrypt:/etc/letsencrypt:ro
    environment:
      - SB_API_PORT=8080
      - SB_CERTIFICATE_FILE=/etc/letsencrypt/live/your-domain.com/fullchain.pem
      - SB_PRIVATE_KEY_FILE=/etc/letsencrypt/live/your-domain.com/privkey.pem
    networks:
      - outline-network

  watchtower:
    image: containrrr/watchtower
    container_name: outline-watchtower
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: --interval 86400 outline-server
    networks:
      - outline-network

volumes:
  outline-data:

networks:
  outline-network:
    driver: bridge

Outline Manager Configuration

Initial Setup and Server Connection

bash
# Download Outline Manager
# Windows: https://github.com/Jigsaw-Code/outline-client/releases/latest/download/Outline-Manager.exe
# macOS: https://github.com/Jigsaw-Code/outline-client/releases/latest/download/Outline-Manager.dmg
# Linux: https://github.com/Jigsaw-Code/outline-client/releases/latest/download/Outline-Manager.AppImage

# Connect to existing server using management API
# Format: https://server-ip:8080/management-api-url
# Example connection string from server installation:
# {"apiUrl":"https://192.168.1.100:8080/management-api-url","certSha256":"certificate-fingerprint"}

# Verify server connection
curl -k -H "Content-Type: application/json" \
  "https://your-server-ip:8080/management-api-url/server"

# Get server information
curl -k -H "Content-Type: application/json" \
  "https://your-server-ip:8080/management-api-url/server/info"

# Example response:
# {
#   "name": "Outline Server",
#   "serverId": "server-id-123",
#   "metricsEnabled": true,
#   "createdTimestampMs": 1640995200000,
#   "version": "1.8.0",
#   "accessKeyDataLimit": null,
#   "portForNewAccessKeys": 443
# }

Access Key Management

bash
# Create new access key
curl -k -X POST \
  -H "Content-Type: application/json" \
  "https://your-server-ip:8080/management-api-url/access-keys"

# Example response:
# {
#   "id": "0",
#   "name": "User 1",
#   "password": "password123",
#   "port": 443,
#   "method": "chacha20-ietf-poly1305",
#   "accessUrl": "ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTpwYXNzd29yZDEyMw@192.168.1.100:443/?outline=1"
# }

# List all access keys
curl -k -H "Content-Type: application/json" \
  "https://your-server-ip:8080/management-api-url/access-keys"

# Rename access key
curl -k -X PUT \
  -H "Content-Type: application/json" \
  -d '{"name": "John Doe"}' \
  "https://your-server-ip:8080/management-api-url/access-keys/0/name"

# Set data limit for access key (in bytes)
curl -k -X PUT \
  -H "Content-Type: application/json" \
  -d '{"limit": {"bytes": 10737418240}}' \
  "https://your-server-ip:8080/management-api-url/access-keys/0/data-limit"

# Remove data limit
curl -k -X DELETE \
  "https://your-server-ip:8080/management-api-url/access-keys/0/data-limit"

# Delete access key
curl -k -X DELETE \
  "https://your-server-ip:8080/management-api-url/access-keys/0"

# Get access key metrics
curl -k -H "Content-Type: application/json" \
  "https://your-server-ip:8080/management-api-url/metrics/transfer"

# Example metrics response:
# {
#   "bytesTransferredByUserId": {
#     "0": 1073741824,
#     "1": 2147483648
#   }
# }

Server Configuration

bash
# Change server name
curl -k -X PUT \
  -H "Content-Type: application/json" \
  -d '{"name": "Company VPN Server"}' \
  "https://your-server-ip:8080/management-api-url/server/name"

# Set default port for new access keys
curl -k -X PUT \
  -H "Content-Type: application/json" \
  -d '{"port": 8443}' \
  "https://your-server-ip:8080/management-api-url/server/port-for-new-access-keys"

# Enable/disable metrics collection
curl -k -X PUT \
  -H "Content-Type: application/json" \
  -d '{"metricsEnabled": true}' \
  "https://your-server-ip:8080/management-api-url/server/metrics-enabled"

# Set server-wide data limit
curl -k -X PUT \
  -H "Content-Type: application/json" \
  -d '{"limit": {"bytes": 107374182400}}' \
  "https://your-server-ip:8080/management-api-url/server/access-key-data-limit"

# Get server configuration
curl -k -H "Content-Type: application/json" \
  "https://your-server-ip:8080/management-api-url/server"

Client Configuration and Usage

Windows Client Setup

powershell
# Download and install Outline Client for Windows
$OutlineClientUrl = "https://github.com/Jigsaw-Code/outline-client/releases/latest/download/Outline-Client.exe"
$OutlineClientPath = "$env:TEMP\Outline-Client.exe"

Invoke-WebRequest -Uri $OutlineClientUrl -OutFile $OutlineClientPath
Start-Process -FilePath $OutlineClientPath -Wait

# Import access key via command line (if supported)
# Note: Outline Client primarily uses GUI for key import
# Access key format: ss://base64-encoded-config@server:port/?outline=1

# Example access key:
# ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTpwYXNzd29yZDEyMw@192.168.1.100:443/?outline=1

# Registry settings for Outline Client (advanced configuration)
$OutlineRegPath = "HKCU:\Software\Outline"
if (!(Test-Path $OutlineRegPath)) {
    New-Item -Path $OutlineRegPath -Force
}

# Set auto-connect on startup
Set-ItemProperty -Path $OutlineRegPath -Name "AutoConnect" -Value 1

# Configure proxy settings
Set-ItemProperty -Path $OutlineRegPath -Name "ProxyMode" -Value "auto"

macOS Client Setup

bash
# Download Outline Client for macOS
curl -L -o "Outline-Client.dmg" \
  "https://github.com/Jigsaw-Code/outline-client/releases/latest/download/Outline-Client.dmg"

# Mount and install
hdiutil mount Outline-Client.dmg
cp -R "/Volumes/Outline Client/Outline Client.app" /Applications/
hdiutil unmount "/Volumes/Outline Client"

# Launch Outline Client
open "/Applications/Outline Client.app"

# Import access key via URL scheme (if supported)
open "ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTpwYXNzd29yZDEyMw@192.168.1.100:443/?outline=1"

# Configure system proxy settings
networksetup -setautoproxyurl "Wi-Fi" "http://127.0.0.1:1080/proxy.pac"

# Check VPN connection status
scutil --nc list | grep -i outline

Linux Client Setup

bash
# Download Outline Client AppImage
wget -O Outline-Client.AppImage \
  "https://github.com/Jigsaw-Code/outline-client/releases/latest/download/Outline-Client.AppImage"

chmod +x Outline-Client.AppImage

# Run Outline Client
./Outline-Client.AppImage

# Alternative: Install via Snap
sudo snap install outline-client

# Create desktop entry
cat > ~/.local/share/applications/outline-client.desktop << 'EOF'
[Desktop Entry]
Name=Outline Client
Comment=Outline VPN Client
Exec=/path/to/Outline-Client.AppImage
Icon=outline-client
Terminal=false
Type=Application
Categories=Network;Security;
EOF

# Configure system-wide proxy (Ubuntu/Debian)
gsettings set org.gnome.system.proxy mode 'auto'
gsettings set org.gnome.system.proxy autoconfig-url 'http://127.0.0.1:1080/proxy.pac'

# Configure iptables for VPN traffic (advanced)
sudo iptables -t nat -A OUTPUT -p tcp --dport 80 -j REDIRECT --to-port 1080
sudo iptables -t nat -A OUTPUT -p tcp --dport 443 -j REDIRECT --to-port 1080

Mobile Client Configuration

bash
# iOS Configuration
# 1. Download Outline app from App Store
# 2. Tap "Add Server" or "+"
# 3. Scan QR code or paste access key
# 4. Tap "Connect"

# Generate QR code for access key (server-side)
qrencode -t PNG -o access-key-qr.png \
  "ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTpwYXNzd29yZDEyMw@192.168.1.100:443/?outline=1"

# Android Configuration
# 1. Download Outline app from Google Play Store
# 2. Tap "Add Server"
# 3. Scan QR code or manually enter access key
# 4. Tap "Connect"

# Android ADB commands for automation (requires root)
adb shell am start -a android.intent.action.VIEW \
  -d "ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTpwYXNzd29yZDEyMw@192.168.1.100:443/?outline=1" \
  org.outline.android.client

Advanced Configuration

Custom Shadowsocks Configuration

bash
# Manual Shadowsocks server configuration
cat > /etc/shadowsocks-libev/config.json << 'EOF'
{
    "server": "0.0.0.0",
    "server_port": 443,
    "password": "your-strong-password",
    "timeout": 300,
    "method": "chacha20-ietf-poly1305",
    "fast_open": false,
    "workers": 1,
    "prefer_ipv6": false,
    "no_delay": true,
    "reuse_port": true,
    "mode": "tcp_and_udp"
}
EOF

# Install Shadowsocks-libev
sudo apt update
sudo apt install shadowsocks-libev

# Start Shadowsocks server
sudo systemctl enable shadowsocks-libev
sudo systemctl start shadowsocks-libev

# Configure multiple ports for load balancing
cat > /etc/shadowsocks-libev/multi-port.json << 'EOF'
{
    "server": "0.0.0.0",
    "port_password": {
        "443": "password1",
        "8443": "password2",
        "9443": "password3"
    },
    "timeout": 300,
    "method": "chacha20-ietf-poly1305",
    "fast_open": false
}
EOF

# Advanced obfuscation with simple-obfs
sudo apt install simple-obfs

cat > /etc/shadowsocks-libev/obfs-config.json << 'EOF'
{
    "server": "0.0.0.0",
    "server_port": 443,
    "password": "your-password",
    "timeout": 300,
    "method": "chacha20-ietf-poly1305",
    "plugin": "obfs-server",
    "plugin_opts": "obfs=tls;obfs-host=www.google.com"
}
EOF

Load Balancing and High Availability

bash
# HAProxy configuration for multiple Outline servers
cat > /etc/haproxy/haproxy.cfg << 'EOF'
global
    daemon
    maxconn 4096

defaults
    mode tcp
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

frontend outline_frontend
    bind *:443
    default_backend outline_servers

backend outline_servers
    balance roundrobin
    server outline1 192.168.1.101:443 check
    server outline2 192.168.1.102:443 check
    server outline3 192.168.1.103:443 check
EOF

# Start HAProxy
sudo systemctl enable haproxy
sudo systemctl start haproxy

# Nginx stream proxy for load balancing
cat > /etc/nginx/nginx.conf << 'EOF'
events {
    worker_connections 1024;
}

stream {
    upstream outline_backend {
        server 192.168.1.101:443;
        server 192.168.1.102:443;
        server 192.168.1.103:443;
    }

    server {
        listen 443;
        proxy_pass outline_backend;
        proxy_timeout 1s;
        proxy_responses 1;
    }
}
EOF

# Keepalived for high availability
cat > /etc/keepalived/keepalived.conf << 'EOF'
vrrp_script chk_outline {
    script "/usr/local/bin/check_outline.sh"
    interval 2
    weight -2
    fall 3
    rise 2
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 101
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass outline123
    }
    virtual_ipaddress {
        192.168.1.100
    }
    track_script {
        chk_outline
    }
}
EOF

# Health check script
cat > /usr/local/bin/check_outline.sh << 'EOF'
#!/bin/bash
curl -k --connect-timeout 5 https://localhost:8080/management-api-url/server >/dev/null 2>&1
exit $?
EOF

chmod +x /usr/local/bin/check_outline.sh

Security Hardening

bash
# Firewall configuration with iptables
#!/bin/bash
# Flush existing rules
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X

# Default policies
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

# Allow loopback
iptables -A INPUT -i lo -j ACCEPT

# Allow established connections
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Allow SSH (change port as needed)
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# Allow Outline VPN
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -p udp --dport 443 -j ACCEPT
iptables -A INPUT -p tcp --dport 8080 -s 192.168.1.0/24 -j ACCEPT

# Enable NAT for VPN clients
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
iptables -A FORWARD -i tun+ -j ACCEPT
iptables -A FORWARD -o tun+ -j ACCEPT

# Rate limiting for SSH
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 -j DROP

# Save rules
iptables-save > /etc/iptables/rules.v4

# Fail2ban configuration for additional security
cat > /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 3

[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log

[outline-api]
enabled = true
port = 8080
filter = outline-api
logpath = /var/log/outline/access.log
maxretry = 5
EOF

# Create custom filter for Outline API
cat > /etc/fail2ban/filter.d/outline-api.conf << 'EOF'
[Definition]
failregex = ^<HOST> - - \[.*\] ".*" 40[13] .*$
ignoreregex =
EOF

# SSL/TLS certificate with Let's Encrypt
sudo apt install certbot

# Generate certificate
sudo certbot certonly --standalone -d your-domain.com

# Configure automatic renewal
echo "0 12 * * * /usr/bin/certbot renew --quiet" | sudo crontab -

# Update Outline server to use SSL certificate
sudo docker exec outline-server \
  sh -c 'echo "SB_CERTIFICATE_FILE=/etc/letsencrypt/live/your-domain.com/fullchain.pem" >> /opt/outline/environment'
sudo docker exec outline-server \
  sh -c 'echo "SB_PRIVATE_KEY_FILE=/etc/letsencrypt/live/your-domain.com/privkey.pem" >> /opt/outline/environment'

sudo docker restart outline-server

Monitoring and Troubleshooting

Server Monitoring

bash
# Monitor Outline server logs
sudo docker logs -f outline-server

# Monitor system resources
htop
iotop
nethogs

# Check network connections
sudo netstat -tulpn | grep :443
sudo ss -tulpn | grep :443

# Monitor bandwidth usage
vnstat -i eth0
iftop -i eth0

# Custom monitoring script
cat > /usr/local/bin/outline-monitor.sh << 'EOF'
#!/bin/bash

LOG_FILE="/var/log/outline-monitor.log"
API_URL="https://localhost:8080/management-api-url"

# Function to log with timestamp
log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> $LOG_FILE
}

# Check server health
check_server_health() {
    if curl -k -s "$API_URL/server" >/dev/null 2>&1; then
        log_message "Server health check: OK"
        return 0
    else
        log_message "Server health check: FAILED"
        return 1
    fi
}

# Check Docker container status
check_container_status() {
    if docker ps | grep -q outline-server; then
        log_message "Container status: Running"
        return 0
    else
        log_message "Container status: Not running"
        return 1
    fi
}

# Get transfer metrics
get_transfer_metrics() {
    METRICS=$(curl -k -s "$API_URL/metrics/transfer" 2>/dev/null)
    if [ $? -eq 0 ]; then
        log_message "Transfer metrics: $METRICS"
    else
        log_message "Failed to retrieve transfer metrics"
    fi
}

# Main monitoring loop
main() {
    log_message "Starting Outline monitoring"
    
    if ! check_container_status; then
        log_message "Attempting to restart Outline server"
        docker restart outline-server
        sleep 10
    fi
    
    if check_server_health; then
        get_transfer_metrics
    else
        log_message "Server health check failed, investigating..."
        docker logs --tail 50 outline-server >> $LOG_FILE
    fi
}

main
EOF

chmod +x /usr/local/bin/outline-monitor.sh

# Add to crontab for regular monitoring
echo "*/5 * * * * /usr/local/bin/outline-monitor.sh" | crontab -

Performance Optimization

bash
# Optimize TCP settings for VPN performance
cat > /etc/sysctl.d/99-outline-performance.conf << 'EOF'
# TCP optimization
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq

# Network buffer optimization
net.core.netdev_max_backlog = 5000
net.core.netdev_budget = 600

# Connection tracking optimization
net.netfilter.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_tcp_timeout_established = 7200

# IP forwarding and routing
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
EOF

# Apply settings
sudo sysctl -p /etc/sysctl.d/99-outline-performance.conf

# Docker container resource limits
sudo docker update --memory=1g --cpus=2 outline-server

# Optimize Shadowsocks configuration for performance
cat > /opt/outline/shadowsocks-optimized.json << 'EOF'
{
    "server": "0.0.0.0",
    "server_port": 443,
    "password": "your-password",
    "timeout": 300,
    "method": "chacha20-ietf-poly1305",
    "fast_open": true,
    "workers": 4,
    "prefer_ipv6": false,
    "no_delay": true,
    "reuse_port": true,
    "mode": "tcp_and_udp"
}
EOF

Troubleshooting Common Issues

bash
# Connection issues troubleshooting
# 1. Check server accessibility
ping your-server-ip
telnet your-server-ip 443

# 2. Verify DNS resolution
nslookup your-domain.com
dig your-domain.com

# 3. Check firewall rules
sudo iptables -L -n
sudo ufw status

# 4. Test port connectivity
nc -zv your-server-ip 443
nc -zuv your-server-ip 443

# Client connection debugging
# Enable debug logging in Outline client
# Windows: %APPDATA%\Outline\logs\
# macOS: ~/Library/Logs/Outline/
# Linux: ~/.config/Outline/logs/

# Server-side debugging
sudo docker exec -it outline-server /bin/bash
cat /var/log/shadowbox.log

# Network troubleshooting
# Check routing table
ip route show
route -n

# Monitor network traffic
sudo tcpdump -i any port 443
sudo tcpdump -i any host your-client-ip

# Check for packet loss
mtr your-server-ip

# Bandwidth testing
iperf3 -s  # On server
iperf3 -c your-server-ip  # On client

# Certificate issues
openssl s_client -connect your-server-ip:443 -servername your-domain.com
openssl x509 -in /etc/letsencrypt/live/your-domain.com/fullchain.pem -text -noout

# Reset Outline server configuration
sudo docker stop outline-server
sudo docker rm outline-server
sudo rm -rf /opt/outline/
# Reinstall using installation script

Automation and Scripting

Automated Deployment Script

bash
#!/bin/bash
# Automated Outline VPN deployment script

set -e

# Configuration variables
SERVER_NAME="Outline VPN Server"
DOMAIN_NAME=""
EMAIL=""
CLOUD_PROVIDER="digitalocean"  # digitalocean, aws, gcp, vultr
REGION="nyc3"
SIZE="s-1vcpu-1gb"

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# Logging function
log() {
    echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"
}

error() {
    echo -e "${RED}[ERROR]${NC} $1" >&2
    exit 1
}

warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}

# Check prerequisites
check_prerequisites() {
    log "Checking prerequisites..."
    
    # Check if running as root
    if [[ $EUID -eq 0 ]]; then
        error "This script should not be run as root"
    fi
    
    # Check required commands
    for cmd in curl docker docker-compose; do
        if ! command -v $cmd &> /dev/null; then
            error "$cmd is required but not installed"
        fi
    done
    
    log "Prerequisites check passed"
}

# Install Docker if not present
install_docker() {
    if ! command -v docker &> /dev/null; then
        log "Installing Docker..."
        curl -fsSL https://get.docker.com -o get-docker.sh
        sh get-docker.sh
        sudo usermod -aG docker $USER
        rm get-docker.sh
        log "Docker installed successfully"
    fi
}

# Deploy Outline server
deploy_outline() {
    log "Deploying Outline server..."
    
    # Create outline directory
    sudo mkdir -p /opt/outline
    cd /opt/outline
    
    # Download and run installation script
    curl -sS https://raw.githubusercontent.com/Jigsaw-Code/outline-server/master/src/server_manager/install_scripts/install_server.sh | sudo bash
    
    # Wait for server to start
    sleep 30
    
    # Check if server is running
    if sudo docker ps | grep -q outline-server; then
        log "Outline server deployed successfully"
    else
        error "Failed to deploy Outline server"
    fi
}

# Configure SSL certificate
configure_ssl() {
    if [[ -n "$DOMAIN_NAME" && -n "$EMAIL" ]]; then
        log "Configuring SSL certificate for $DOMAIN_NAME..."
        
        # Install certbot
        sudo apt update
        sudo apt install -y certbot
        
        # Stop outline server temporarily
        sudo docker stop outline-server
        
        # Generate certificate
        sudo certbot certonly --standalone -d $DOMAIN_NAME --email $EMAIL --agree-tos --non-interactive
        
        # Update outline configuration
        sudo docker exec outline-server \
            sh -c "echo 'SB_CERTIFICATE_FILE=/etc/letsencrypt/live/$DOMAIN_NAME/fullchain.pem' >> /opt/outline/environment"
        sudo docker exec outline-server \
            sh -c "echo 'SB_PRIVATE_KEY_FILE=/etc/letsencrypt/live/$DOMAIN_NAME/privkey.pem' >> /opt/outline/environment"
        
        # Restart outline server
        sudo docker start outline-server
        
        # Setup auto-renewal
        echo "0 12 * * * /usr/bin/certbot renew --quiet && docker restart outline-server" | sudo crontab -
        
        log "SSL certificate configured successfully"
    else
        warning "Domain name or email not provided, skipping SSL configuration"
    fi
}

# Create access keys
create_access_keys() {
    log "Creating initial access keys..."
    
    # Wait for API to be ready
    sleep 10
    
    # Get API URL from server
    API_URL=$(sudo cat /opt/outline/access.txt | grep -o 'https://[^"]*')
    
    if [[ -n "$API_URL" ]]; then
        # Create 3 initial access keys
        for i in {1..3}; do
            RESPONSE=$(curl -k -X POST -H "Content-Type: application/json" "$API_URL/access-keys" 2>/dev/null)
            if [[ $? -eq 0 ]]; then
                KEY_ID=$(echo $RESPONSE | grep -o '"id":"[^"]*' | cut -d'"' -f4)
                ACCESS_URL=$(echo $RESPONSE | grep -o '"accessUrl":"[^"]*' | cut -d'"' -f4)
                
                # Rename the key
                curl -k -X PUT -H "Content-Type: application/json" \
                    -d "{\"name\": \"User $i\"}" \
                    "$API_URL/access-keys/$KEY_ID/name" 2>/dev/null
                
                log "Created access key for User $i: $ACCESS_URL"
            fi
        done
    else
        error "Failed to get API URL"
    fi
}

# Setup monitoring
setup_monitoring() {
    log "Setting up monitoring..."
    
    # Create monitoring script
    sudo tee /usr/local/bin/outline-health-check.sh > /dev/null << 'EOF'
#!/bin/bash
API_URL=$(cat /opt/outline/access.txt | grep -o 'https://[^"]*')
if curl -k -s "$API_URL/server" >/dev/null 2>&1; then
    echo "$(date): Outline server is healthy"
else
    echo "$(date): Outline server health check failed"
    docker restart outline-server
fi
EOF
    
    sudo chmod +x /usr/local/bin/outline-health-check.sh
    
    # Add to crontab
    echo "*/5 * * * * /usr/local/bin/outline-health-check.sh >> /var/log/outline-health.log 2>&1" | sudo crontab -
    
    log "Monitoring setup completed"
}

# Main deployment function
main() {
    log "Starting Outline VPN deployment..."
    
    check_prerequisites
    install_docker
    deploy_outline
    configure_ssl
    create_access_keys
    setup_monitoring
    
    log "Outline VPN deployment completed successfully!"
    log "Server management information:"
    sudo cat /opt/outline/access.txt
}

# Parse command line arguments
while [[ $# -gt 0 ]]; do
    case $1 in
        -d|--domain)
            DOMAIN_NAME="$2"
            shift 2
            ;;
        -e|--email)
            EMAIL="$2"
            shift 2
            ;;
        -h|--help)
            echo "Usage: $0 [OPTIONS]"
            echo "Options:"
            echo "  -d, --domain DOMAIN    Domain name for SSL certificate"
            echo "  -e, --email EMAIL      Email for Let's Encrypt"
            echo "  -h, --help            Show this help message"
            exit 0
            ;;
        *)
            error "Unknown option: $1"
            ;;
    esac
done

# Run main function
main

Bulk User Management

bash
#!/bin/bash
# Bulk user management script for Outline VPN

API_URL=$(cat /opt/outline/access.txt | grep -o 'https://[^"]*')
USERS_FILE="users.csv"

# Create users from CSV file
# CSV format: username,data_limit_gb
create_users_from_csv() {
    if [[ ! -f "$USERS_FILE" ]]; then
        echo "Users file not found: $USERS_FILE"
        exit 1
    fi
    
    while IFS=',' read -r username data_limit_gb; do
        # Skip header line
        if [[ "$username" == "username" ]]; then
            continue
        fi
        
        echo "Creating user: $username"
        
        # Create access key
        RESPONSE=$(curl -k -X POST -H "Content-Type: application/json" "$API_URL/access-keys" 2>/dev/null)
        KEY_ID=$(echo $RESPONSE | grep -o '"id":"[^"]*' | cut -d'"' -f4)
        ACCESS_URL=$(echo $RESPONSE | grep -o '"accessUrl":"[^"]*' | cut -d'"' -f4)
        
        # Set username
        curl -k -X PUT -H "Content-Type: application/json" \
            -d "{\"name\": \"$username\"}" \
            "$API_URL/access-keys/$KEY_ID/name" 2>/dev/null
        
        # Set data limit if specified
        if [[ -n "$data_limit_gb" && "$data_limit_gb" != "unlimited" ]]; then
            DATA_LIMIT_BYTES=$((data_limit_gb * 1024 * 1024 * 1024))
            curl -k -X PUT -H "Content-Type: application/json" \
                -d "{\"limit\": {\"bytes\": $DATA_LIMIT_BYTES}}" \
                "$API_URL/access-keys/$KEY_ID/data-limit" 2>/dev/null
        fi
        
        echo "User $username created with key ID: $KEY_ID"
        echo "Access URL: $ACCESS_URL"
        echo "---"
        
    done < "$USERS_FILE"
}

# Generate usage report
generate_usage_report() {
    echo "Generating usage report..."
    
    # Get all access keys
    KEYS=$(curl -k -s "$API_URL/access-keys" 2>/dev/null)
    
    # Get transfer metrics
    METRICS=$(curl -k -s "$API_URL/metrics/transfer" 2>/dev/null)
    
    echo "User Usage Report - $(date)"
    echo "================================"
    printf "%-20s %-10s %-15s\n" "Username" "Key ID" "Data Used (MB)"
    echo "--------------------------------"
    
    # Parse and display usage for each user
    echo "$KEYS" | jq -r '.accessKeys[] | "\(.id)|\(.name)"' | while IFS='|' read -r key_id username; do
        BYTES_USED=$(echo "$METRICS" | jq -r ".bytesTransferredByUserId.\"$key_id\" // 0")
        MB_USED=$((BYTES_USED / 1024 / 1024))
        printf "%-20s %-10s %-15s\n" "$username" "$key_id" "$MB_USED"
    done
}

# Backup access keys
backup_access_keys() {
    BACKUP_FILE="outline-backup-$(date +%Y%m%d-%H%M%S).json"
    
    echo "Creating backup: $BACKUP_FILE"
    
    # Get all access keys and server info
    KEYS=$(curl -k -s "$API_URL/access-keys" 2>/dev/null)
    SERVER_INFO=$(curl -k -s "$API_URL/server" 2>/dev/null)
    
    # Create backup JSON
    jq -n \
        --argjson keys "$KEYS" \
        --argjson server "$SERVER_INFO" \
        '{
            "backup_date": now | strftime("%Y-%m-%d %H:%M:%S"),
            "server_info": $server,
            "access_keys": $keys
        }' > "$BACKUP_FILE"
    
    echo "Backup created: $BACKUP_FILE"
}

# Show usage
show_usage() {
    echo "Usage: $0 [COMMAND]"
    echo "Commands:"
    echo "  create-users    Create users from CSV file"
    echo "  usage-report    Generate usage report"
    echo "  backup         Backup access keys"
    echo "  help           Show this help message"
}

# Main script logic
case "$1" in
    create-users)
        create_users_from_csv
        ;;
    usage-report)
        generate_usage_report
        ;;
    backup)
        backup_access_keys
        ;;
    help|--help|-h)
        show_usage
        ;;
    *)
        echo "Unknown command: $1"
        show_usage
        exit 1
        ;;
esac

Resources