Complete development environment setup script for Ubuntu 24.04 LTS VPS, optimized for Claude Code and multi-stack development.
- β tmux - Terminal multiplexer with custom config
- β git - Version control with useful aliases
- β vim/nano - Text editors
- β htop/btop - System monitoring
- β ncdu - Disk usage analyzer
- β tree, jq, zip/unzip - File utilities
- β Docker - Latest stable version
- β Docker Compose - V2 plugin
- β Docker Buildx - Multi-platform builds
- β NVM v0.39.7 - Node Version Manager
- β Node.js v20.19.4 (LTS)
- β npm - Latest version
- β pnpm - Fast package manager
- β yarn - Alternative package manager
- β PM2 - Process manager
- β NestJS CLI - For NestJS projects
- β TypeScript/ts-node - TypeScript tooling
- β .NET 8 SDK - Complete development kit
- β .NET 8 Runtime - Runtime environment
- β ASP.NET Core Runtime - Web development
- β Python 3.12 - Latest Ubuntu 24.04 default
- β pip - Package installer
- β venv/virtualenv - Environment management
- β pipenv/poetry - Advanced package managers
- β IPython/Jupyter - Interactive shells
- β black/flake8/pylint - Code quality tools
- β PHP 8.3 - Latest stable
- β Composer - Dependency manager
- β Extensions: MySQL, PostgreSQL, Redis, GD, curl, XML, BCMath, etc.
- β OpenJDK 17 - Java Development Kit (required for Android)
- β Android SDK - Command-line tools for building Android apps
- β Android Build Tools 35.0.0 - Latest build tools
- β Android Platform Tools - ADB and fastboot
- β Android Platforms - SDK platforms 34 & 35
- β Gradle 8.12 - Build automation tool
- β MySQL client
- β PostgreSQL client
- β Redis CLI
- β SQLite
- β mongosh - MongoDB Shell
- β Claude Code CLI - AI-assisted coding
- β GitHub CLI (gh) - GitHub operations
- β lazygit - Beautiful Git UI
- β fd - Modern find replacement
- β ripgrep (rg) - Fast grep alternative
- β bat - Cat with syntax highlighting
- β exa - Modern ls replacement
- β Shell-GPT (sgpt) - Simple CLI assistant for quick commands
- β Aider - AI pair programming tool
- β Ollama - Local LLM runtime for offline AI
- β GitHub Copilot CLI - GitHub's AI CLI extension
- β Codex CLI - OpenAI's code assistant
- π¦ OpenClaw - Personal AI assistant via messaging channels (see OpenClaw Setup)
- β UFW firewall - Configured with SSH/HTTP/HTTPS
- β fail2ban - Intrusion prevention
- β certbot - SSL certificate management
# From your local machine (replace with your VPS IP)
scp vps-dev-setup.sh user@your-vps-ip:~/
# OR copy-paste the content directly
nano vps-dev-setup.sh
# Paste content, Ctrl+O to save, Ctrl+X to exitchmod +x vps-dev-setup.sh./vps-dev-setup.shβ±οΈ Estimated time: 10-15 minutes depending on connection speed
# 1. Log out and back in (or source bashrc)
source ~/.bashrc
# 2. Verify Node.js installation
node --version # Should show v20.19.4
# 3. Configure Git
git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"
# 4. Set up SSH keys for GitHub
ssh-keygen -t ed25519 -C "your.email@example.com"
cat ~/.ssh/id_ed25519.pub
# Add to GitHub: https://github.com/settings/keys
# 5. Authenticate Claude Code
claude-code auth
# 6. Test Docker (after re-login)
docker run hello-worldTo clone and work with private GitHub repositories on your VPS, you need to set up authentication. Here are the recommended methods:
SSH keys provide secure, password-less authentication for all your GitHub repositories.
# Generate a new ED25519 SSH key (recommended)
ssh-keygen -t ed25519 -C "your.email@example.com"
# Or use RSA if ED25519 is not supported
ssh-keygen -t rsa -b 4096 -C "your.email@example.com"When prompted:
- Press Enter to accept the default file location (
~/.ssh/id_ed25519) - Enter a passphrase for extra security (optional but recommended)
# Display your public key
cat ~/.ssh/id_ed25519.pubCopy the output and add it to GitHub:
- Go to GitHub SSH Settings
- Click New SSH key
- Give it a descriptive title (e.g., "VPS Development Server")
- Paste your public key
- Click Add SSH key
# Test SSH connection to GitHub
ssh -T git@github.comYou should see: Hi username! You've successfully authenticated...
# Clone using SSH URL (starts with git@github.com:)
git clone git@github.com:username/private-repo.git
# Or update existing repo to use SSH
git remote set-url origin git@github.com:username/private-repo.gitThe GitHub CLI (gh) provides easy authentication with browser-based login.
# Start authentication
gh auth login
# Follow the prompts:
# 1. Select GitHub.com
# 2. Select SSH as preferred protocol
# 3. Upload your SSH key (or generate a new one)
# 4. Complete browser-based authentication
# Verify authentication
gh auth status
# Clone private repositories
gh repo clone username/private-repoUse deploy keys when you need read-only access to specific repositories (useful for CI/CD or automated deployments).
# Generate a dedicated key for the specific repo
ssh-keygen -t ed25519 -C "deploy-key-myproject" -f ~/.ssh/deploy_myproject
# Display the public key
cat ~/.ssh/deploy_myproject.pubAdd the deploy key to your repository:
- Go to your repository on GitHub
- Navigate to Settings β Deploy keys
- Click Add deploy key
- Paste the public key and give it a title
- Check "Allow write access" if needed
Configure SSH to use the deploy key:
# Add to ~/.ssh/config
cat >> ~/.ssh/config << 'EOF'
Host github-myproject
HostName github.com
User git
IdentityFile ~/.ssh/deploy_myproject
IdentitiesOnly yes
EOF
# Clone using the custom host alias
git clone git@github-myproject:username/private-repo.gitUse tokens for HTTPS-based authentication (useful for scripts and automation).
- Go to GitHub Token Settings
- Click Generate new token (classic) or Fine-grained tokens
- Select scopes:
repo(for full repository access) - Copy the generated token
# Option A: Use credential helper to cache token
git config --global credential.helper store
# Clone a repo and enter token when prompted for password
git clone https://github.com/username/private-repo.git
# Username: your-github-username
# Password: paste-your-token-here
# Option B: Include token in remote URL (less secure, avoid for shared systems)
git clone https://YOUR_TOKEN@github.com/username/private-repo.git
β οΈ Security Warning: Tokens in URLs may appear in logs. Use SSH keys or credential helpers instead for better security.
- Use SSH Keys - Most secure and convenient for regular development work
- Use Deploy Keys - For automated systems that need access to specific repositories
- Use Tokens Sparingly - Only for scripts that require HTTPS; rotate them regularly
- Protect Private Keys - Never share or commit your private keys (
id_ed25519, not.pub) - Use Passphrases - Add a passphrase to SSH keys for extra security
- Use SSH Agent - Avoid entering passphrases repeatedly
# Start SSH agent
eval "$(ssh-agent -s)"
# Add your key (you'll enter passphrase once per session)
ssh-add ~/.ssh/id_ed25519
# Verify loaded keys
ssh-add -lTo automatically start SSH agent, add to your ~/.bashrc:
# Auto-start SSH agent
if [ -z "$SSH_AUTH_SOCK" ]; then
eval "$(ssh-agent -s)" > /dev/null
ssh-add ~/.ssh/id_ed25519 2>/dev/null
fi# Check if SSH agent has your key
ssh-add -l
# If empty, add your key
ssh-add ~/.ssh/id_ed25519
# Verify the key is in GitHub
curl -s https://api.github.com/users/YOUR_USERNAME/keys
# Test with verbose output
ssh -vT git@github.com# Add GitHub's host key to known hosts
ssh-keyscan github.com >> ~/.ssh/known_hostsIf you use multiple GitHub accounts, configure SSH with different hosts:
# ~/.ssh/config
Host github-personal
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_personal
IdentitiesOnly yes
Host github-work
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_work
IdentitiesOnly yesUse the appropriate host when cloning:
git clone git@github-personal:personal-account/repo.git
git clone git@github-work:work-org/repo.gitThe script creates the following directory structure:
~/
βββ projects/
β βββ web/ # Web application projects
β βββ api/ # API/Backend projects
β βββ mobile/ # Mobile app projects
β βββ automation/ # Automation scripts
β βββ experiments/ # Testing & experiments
βββ backups/ # Local backups
βββ scripts/ # Utility scripts
βββ logs/ # Application logs
The script configures these aliases (available after source ~/.bashrc):
.. # cd ..
... # cd ../..
.... # cd ../../..gs # git status
ga # git add
gc # git commit
gp # git push
gl # git log --oneline --graph
gco # git checkoutd # docker
dc # docker compose
dps # docker ps
dpa # docker ps -a
dex # docker exec -it
dlogs # docker logs -f
dprune # docker system prune -afni # npm install
ns # npm start
nt # npm test
nr # npm run
pn # pnpmta # tmux attach -t
tl # tmux list-sessions
tn # tmux new -sll # exa -lah (or ls -alh)
lt # tree view
ports # netstat -tulanp
usage # disk usage (du -h -d1)cc # claude-codeai # aider
aic # aider --model sonnet
aig # aider --model gpt-4o
ask # sgpt (Shell-GPT)
ollama-code # ollama run deepseek-coderask-cmd '<query>' # Get shell commands from AI
ask-code '<query>' # Generate code from AI
gac # Git add all + AI commit reviewoc # openclaw
oc-chat # openclaw chat
oc-status # Gateway service status
oc-start # Start Gateway service
oc-stop # Stop Gateway service
oc-restart # Restart Gateway service
oc-logs # View Gateway logs (live)
oc-config # Edit configuration
oc-check # Quick status overviewCustom tmux config with developer-friendly settings:
- Prefix:
Ctrl+A(instead of defaultCtrl+B) - Split Horizontal:
Ctrl+Athen| - Split Vertical:
Ctrl+Athen- - Navigate Panes:
Ctrl+Athenh/j/k/l - Reload Config:
Ctrl+Athenr
- β Mouse support enabled
- β 50,000 line scrollback buffer
- β Window numbering starts at 1
- β Automatic window renumbering
- β 256 color support
# Create new session
tmux new -s work
# Detach (keep session running)
Ctrl+A, then d
# List sessions
tmux ls
# Reattach to session
tmux attach -t work
# Kill session
tmux kill-session -t work- Install Termius from App Store
- Add new host with VPS IP
- Configure SSH key authentication
- Enable "Keep alive" for persistent connections
- Install Blink Shell from App Store
- More terminal-native experience
- Better for long coding sessions
# On VPS: Start tmux session
tmux new -s claude-dev
# Start Claude Code
cd ~/projects/web
claude-code
# On iPhone: Detach when needed
Ctrl+A, d
# On iPhone: Reattach anytime
ssh user@vps-ip
tmux attach -t claude-devThis setup includes a complete Android development environment for building Android apps via command line over SSH.
The following environment variables are configured:
JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
ANDROID_HOME=$HOME/Android/Sdk
ANDROID_SDK_ROOT=$HOME/Android/Sdk# Navigate to your Android project
cd ~/projects/mobile/my-android-app
# Build debug APK
./gradlew assembleDebug
# Build release APK
./gradlew assembleRelease
# Build and install on connected device (via ADB over network)
./gradlew installDebug
# Run all tests
./gradlew test
# Clean and rebuild
./gradlew clean build# List installed packages
sdkmanager --list_installed
# List available packages
sdkmanager --list
# Install additional SDK platforms
sdkmanager "platforms;android-33"
sdkmanager "build-tools;34.0.0"
# Update all installed packages
sdkmanager --update
# Accept licenses
sdkmanager --licenses# List connected devices
adb devices
# Connect to device over network (useful for remote development)
adb connect <device-ip>:5555
# Install APK
adb install app-debug.apk
# Uninstall app
adb uninstall com.example.myapp
# View logs
adb logcat
# Copy files from/to device
adb push local-file.txt /sdcard/
adb pull /sdcard/file.txt ./# Using Gradle init (basic)
mkdir my-android-app && cd my-android-app
gradle init --type basic
# For a complete Android project, clone a template or use Android Studio
# on your local machine, then push to git and clone on VPS# Build React Native Android app
cd my-react-native-app
npx react-native build-android --mode=release
# Expo projects
npx expo prebuild --platform android
cd android && ./gradlew assembleRelease# Install Flutter (if needed)
git clone https://github.com/flutter/flutter.git ~/flutter
export PATH="$PATH:$HOME/flutter/bin"
flutter doctor
# Build Flutter Android app
cd my-flutter-app
flutter build apk --release
flutter build appbundle --release# Build with increased memory
./gradlew assembleDebug -Dorg.gradle.jvmargs="-Xmx4g"
# Parallel builds
./gradlew assembleDebug --parallel
# Offline mode (faster if dependencies cached)
./gradlew assembleDebug --offline
# Skip tests
./gradlew assembleDebug -x test
# Verbose output
./gradlew assembleDebug --infoThe script configures UFW to allow only:
- SSH (port 22)
- HTTP (port 80)
- HTTPS (port 443)
To allow additional ports:
sudo ufw allow 3000/tcp # Example: Node.js app
sudo ufw status # Check current rulesAutomatically enabled to protect against brute-force attacks on SSH.
Check status:
sudo fail2ban-client status
sudo fail2ban-client status sshd# 1. Change default SSH port (optional)
sudo nano /etc/ssh/sshd_config
# Change: Port 22 β Port 2222
sudo systemctl restart sshd
sudo ufw allow 2222/tcp
sudo ufw delete allow 22/tcp
# 2. Disable password authentication (after SSH keys setup)
sudo nano /etc/ssh/sshd_config
# Set: PasswordAuthentication no
sudo systemctl restart sshd
# 3. Set up automatic security updates
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgradesThis environment includes multiple AI CLI tools for different development scenarios.
Configure API keys for AI services:
# Add to ~/.bashrc for persistence
export OPENAI_API_KEY='your-openai-key' # For Shell-GPT, Aider with GPT
export ANTHROPIC_API_KEY='your-anthropic-key' # For Aider with Claude
# Download local models for Ollama (optional, for offline use)
ollama pull deepseek-coder # Best for coding tasks
ollama pull llama3 # General purpose
ollama pull codellama # Code completion# 1. Start a tmux session
tmux new -s dev
# 2. Use Claude Code for large tasks
claude-code
# 3. Use Aider for quick refactoring
aider app.py tests.py
# 4. Use Ollama for offline queries
ollama run deepseek-coder "explain this pattern"
# 5. Use Shell-GPT for quick commands
sgpt "find all TODO comments"Refactoring:
aider --model sonnet app/Test Generation:
aider tests/ --message "add tests for UserController"Bug Fixing:
claude-code # For deep context understandingQuick Commands:
sgpt --code "bash script to backup database"| Tool | Best For | Requires API Key | Offline Support |
|---|---|---|---|
| Claude Code | Complex tasks, deep context | Anthropic | No |
| Aider | Pair programming, refactoring | OpenAI/Anthropic | No |
| Shell-GPT | Quick queries, commands | OpenAI | No |
| Ollama | Offline work, local models | None | Yes |
| GitHub Copilot | GitHub integration | GitHub | No |
# AI Tools
cc # Claude Code
ai / aider # Aider
aic # Aider with Claude Sonnet
aig # Aider with GPT-4o
ask / sgpt # Shell-GPT
ollama-code # Ollama with deepseek-coder
# Helper Functions
ask-cmd '<query>' # Get shell command suggestions
ask-code '<query>' # Generate code snippets
gac # Git add all + AI-assisted commitOpenClaw is an optional add-on that enables you to interact with AI through messaging platforms (WhatsApp, Telegram, Discord, Slack). It runs as a Gateway service on your VPS.
- Message from anywhere: Chat with your AI assistant via your phone's messaging apps
- Persistent sessions: Your conversations continue even when you disconnect
- Multiple channels: Support for WhatsApp, Telegram, Discord, and Slack
- Secure by default: Loopback binding, pairing mode, and sandbox isolation
After running vps-dev-setup.sh, install OpenClaw with the optional script:
# Download and run OpenClaw setup
chmod +x openclaw-setup.sh
./openclaw-setup.sh
# Source bashrc to get aliases
source ~/.bashrc
# Complete onboarding
openclaw onboardNote: OpenClaw requires Node.js v22+. The setup script will upgrade your Node.js if needed (from v20 installed by the main script).
# 1. Set your API key (if not done during onboarding)
openclaw config set apiKey YOUR_ANTHROPIC_API_KEY
# 2. Start the Gateway service
oc-start
# 3. Check status
oc-status
# 4. View logs
oc-logsThe Gateway runs as a systemd user service:
# Start/stop/restart
systemctl --user start openclaw-gateway
systemctl --user stop openclaw-gateway
systemctl --user restart openclaw-gateway
# Check status
systemctl --user status openclaw-gateway
# View logs
journalctl --user -u openclaw-gateway -f
# Or use the aliases:
oc-start / oc-stop / oc-restart / oc-status / oc-logsConfiguration is stored at ~/.openclaw/openclaw.json. Edit with:
oc-config # Opens in nano/your editorDefault secure settings:
- Gateway bound to
127.0.0.1:18789(localhost only) - All messaging channels disabled
- Pairing mode enabled for all channels
- Sandbox mode for non-main sessions
β οΈ Security Warning: Only enable channels you need. Each channel requires explicit configuration to prevent unauthorized access.
# Edit configuration
oc-config
# Find the channel you want to enable and set "enabled": true
# Configure allowFrom lists for authorized contacts
# Restart the gateway
oc-restartFor detailed channel setup, see docs/OPENCLAW.md or visit:
- https://docs.openclaw.ai/channels/whatsapp
- https://docs.openclaw.ai/channels/telegram
- https://docs.openclaw.ai/channels/discord
- https://docs.openclaw.ai/channels/slack
The Gateway binds to localhost only by default. For remote access:
# From your local machine
ssh -L 18789:127.0.0.1:18789 user@your-vps-ip
# Gateway is now accessible at localhost:18789 on your machine# Install Tailscale on VPS and client device
# Update openclaw.json to allow Tailscale:
# "auth": { "allowTailscale": true }~/.openclaw/
βββ openclaw.json # Main configuration
βββ workspace/ # Agent workspace
βββ backups/ # Configuration backups
~/logs/openclaw/
βββ openclaw.log # Application logs
βββ gateway.log # Gateway service logs
βββ gateway-error.log # Gateway error logs
βββ audit.log # Security audit trail
# Commands
oc # openclaw (main CLI)
oc-chat # openclaw chat
oc-code # openclaw code
# Service management
oc-start # Start Gateway
oc-stop # Stop Gateway
oc-restart # Restart Gateway
oc-status # Check Gateway status
oc-enable # Enable Gateway on boot
oc-disable # Disable Gateway on boot
# Logs
oc-logs # Live Gateway logs
oc-logs-all # All Gateway logs
oc-logs-error # Error logs only
# Configuration
oc-config # Edit configuration
oc-check # Quick status check
# Navigation
oc-workspace # Go to workspace directory# Stop and disable the service
oc-stop
systemctl --user disable openclaw-gateway
# Remove the npm package
npm uninstall -g openclaw
# Remove configuration and workspace (optional)
rm -rf ~/.openclaw
rm ~/.config/systemd/user/openclaw-gateway.service
# Remove log files (optional)
rm -rf ~/logs/openclaw
# Remove aliases from .bashrc (manual)
nano ~/.bashrc
# Delete the "OpenClaw Configuration" sectionProblem: permission denied while trying to connect to the Docker daemon socket
Solution:
# Log out and log back in to apply group membership
exit
# SSH back inProblem: nvm: command not found
Solution:
source ~/.bashrc
# OR
source ~/.nvm/nvm.shProblem: Application won't start because port is in use
Solution:
# Find process using port (e.g., 3000)
sudo lsof -i :3000
# OR
sudo netstat -tulanp | grep 3000
# Kill the process
sudo kill -9 <PID>Problem: No space left on device
Solution:
# Analyze disk usage
ncdu /
# Clean Docker
docker system prune -af
# Clean package cache
sudo apt clean
sudo apt autoclean
# Clean old logs
sudo journalctl --vacuum-time=7dProblem: sdkmanager: command not found or similar
Solution:
# Reload shell configuration
source ~/.bashrc
# Or manually add to path
export ANDROID_HOME="$HOME/Android/Sdk"
export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$PATH"Problem: Build fails with OutOfMemoryError or GC overhead limit exceeded
Solution:
# Increase Gradle daemon memory
echo "org.gradle.jvmargs=-Xmx4g -XX:+HeapDumpOnOutOfMemoryError" >> ~/.gradle/gradle.properties
# Or pass directly to build
./gradlew assembleDebug -Dorg.gradle.jvmargs="-Xmx4g"
# For very limited memory VPS, disable daemon
./gradlew assembleDebug --no-daemonProblem: Build fails with license agreement errors
Solution:
# Accept all licenses
yes | sdkmanager --licensesProblem: Gradle/Android build fails with Java not found
Solution:
# Reload shell or set manually
source ~/.bashrc
# Or set explicitly
export JAVA_HOME="/usr/lib/jvm/java-17-openjdk-amd64"
export PATH="$JAVA_HOME/bin:$PATH"Problem: oc-start fails or Gateway service won't start
Solution:
# Check service status for error details
oc-status
# Check logs for specific errors
oc-logs-error
# Verify configuration is valid JSON
openclaw config validate
# Ensure Node.js v22+ is active
node --version
# If not v22+, switch:
nvm use 22
# Restart the service
systemctl --user daemon-reload
oc-restartProblem: openclaw: command not found after installation
Solution:
# Reload NVM and shell
source ~/.nvm/nvm.sh
source ~/.bashrc
# Verify Node.js is using correct version
nvm use 22
# Check if openclaw is in npm global
npm list -g openclaw
# If not found, reinstall
npm install -g openclaw@latestProblem: Gateway fails with "port 18789 already in use"
Solution:
# Find process using the port
sudo lsof -i :18789
# Kill the process
sudo kill -9 <PID>
# Or change the port in configuration
oc-config
# Change "port": 18789 to another port
oc-restartProblem: Sandbox mode errors or Docker-related failures
Solution:
# Verify Docker is running
docker info
# If Docker permission denied, add user to docker group
sudo usermod -aG docker $USER
# Log out and back in
# Check Docker is accessible
docker run hello-world
# Restart OpenClaw Gateway
oc-restart# CPU & Memory (interactive)
htop
# OR modern alternative
btop
# Disk usage
df -h
ncdu /
# Docker stats
docker stats
# System info
inxi -F# All services
systemctl list-units --type=service --state=running
# Specific service
systemctl status docker
systemctl status fail2bansudo apt update
sudo apt upgrade -ynvm install 20.19.4 # or newer version
nvm use 20.19.4
nvm alias default 20.19.4npm update -gsudo apt update
sudo apt upgrade docker-ce docker-ce-cli containerd.io- Check
htopfor resource usage - Monitor Docker containers:
docker ps
- Update system:
sudo apt update && sudo apt upgrade - Clean Docker:
docker system prune - Review logs:
journalctl -xe
- Review disk usage:
ncdu / - Check fail2ban logs:
sudo fail2ban-client status - Update all global packages
If you encounter issues:
- Check troubleshooting section above
- Review script output for error messages
- Check system logs:
journalctl -xe - Verify service status:
systemctl status <service>
This setup script is provided as-is for development purposes.
Last Updated: February 2026
Target System: Ubuntu 24.04 LTS