Skip to content

Air-gapped / On-premise Deployment

💡 TL;DR: Use 5 switches to disable external network requests and run OpenCode in isolated enterprise environments.

📝 Course Notes

Key takeaways from this lesson:

B6 Air-gapped / On-premise Deployment Notes


What You'll Be Able to Do

  • Run OpenCode in completely air-gapped environments with no internet access
  • Use locally cached model lists without relying on models.dev
  • Connect to internal AI gateways without triggering any external network requests
  • Troubleshoot common on-premise issues like "startup hangs" and "network timeouts"
  • Resolve the most common dependency installation hang issue (@opencode-ai/plugin)

Your Current Challenge

  • Corporate network blocks external internet access, OpenCode hangs on startup
  • You set OPENCODE_DISABLE_MODELS_FETCH=true, but it still hangs
  • You don't know which external URLs OpenCode tries to reach
  • You want to use your company's internal AI gateway but don't know how to configure it
  • Core issue: No error messages after startup, but nothing happens. Logs show no "loading plugin" output (typical sign of @opencode-ai/plugin SDK installation hanging)

When to Use This Approach

  • Air-gapped enterprise environments where machines cannot access the internet
  • Security and compliance requirements that prohibit any data leaving the internal network
  • On-premise development environments (isolated data centers, secure facilities)
  • CI/CD pipelines where you want faster startup and want to avoid network flakiness

🎒 Prerequisites

  • [ ] A working internal AI gateway (or local Ollama)
  • [ ] Ability to download https://models.dev/api.json from an internet-connected machine
  • [ ] Familiarity with 1.4 Connect Models basic configuration
  • [ ] (Optional) Verify ripgrep is installed: rg --version

💡 Check ripgrep

In air-gapped environments, verify rg is pre-installed to avoid failures when using AI's grep functionality.


Core Concept

OpenCode makes the following external network requests on startup:

RequestPurposeHow to Disable
models.dev/api.jsonFetch model listOption A (Fully offline): OPENCODE_DISABLE_MODELS_FETCH=true + OPENCODE_MODELS_PATH=...; Option B (Internal mirror): Only set OPENCODE_MODELS_URL=https://... (do NOT set OPENCODE_DISABLE_MODELS_FETCH=true)
npm registryInstall built-in pluginsOPENCODE_DISABLE_DEFAULT_PLUGINS
GitHub releasesCheck for updatesOPENCODE_DISABLE_AUTOUPDATE or autoupdate: false
LSP server downloadsLanguage serversOPENCODE_DISABLE_LSP_DOWNLOAD

📖 Two Model List Options

  • Fully offline: Download models.json, set OPENCODE_MODELS_PATH, and enable OPENCODE_DISABLE_MODELS_FETCH=true
  • Internal mirror: Provide https://<host>/api.json on your internal network, set OPENCODE_MODELS_URL=https://<host>, do NOT set OPENCODE_DISABLE_MODELS_FETCH=true

If both OPENCODE_MODELS_PATH and OPENCODE_MODELS_URL are set, PATH takes priority.

Disable all 4 request types, and OpenCode will run in a fully isolated environment.


Step-by-Step Guide

Step 1: Download the Model List File

Why
OpenCode needs to know which models are available. Download this file from an internet-connected machine first, then copy it to your air-gapped environment.

On an internet-connected machine:

bash
curl -o models.json https://models.dev/api.json

Expected result: A models.json file (~2000+KB) is created in the current directory.

Step 2: Transfer Model List to Air-gapped Machine

Why
The air-gapped machine needs this file to understand model capabilities (context limits, tool_call support, etc.).

Copy models.json to a fixed location on the air-gapped machine:

bash
# Recommended location: ~/.cache/opencode/
mkdir -p ~/.cache/opencode
cp models.json ~/.cache/opencode/models.json

Step 3: Configure Environment Variables

Why
This is the core step. After setting these environment variables, OpenCode won't attempt any external network requests.

bash
export OPENCODE_DISABLE_MODELS_FETCH=true
export OPENCODE_MODELS_PATH=~/.cache/opencode/models.json
export OPENCODE_DISABLE_DEFAULT_PLUGINS=true
export OPENCODE_DISABLE_AUTOUPDATE=true
export OPENCODE_DISABLE_LSP_DOWNLOAD=true
bash
# Add to ~/.bashrc or ~/.zshrc
cat >> ~/.zshrc << 'EOF'
# OpenCode air-gapped configuration
export OPENCODE_DISABLE_MODELS_FETCH=true
export OPENCODE_MODELS_PATH=~/.cache/opencode/models.json
export OPENCODE_DISABLE_DEFAULT_PLUGINS=true
export OPENCODE_DISABLE_AUTOUPDATE=true
export OPENCODE_DISABLE_LSP_DOWNLOAD=true
EOF

source ~/.zshrc
powershell
$env:OPENCODE_DISABLE_MODELS_FETCH = "true"
$env:OPENCODE_MODELS_PATH = "$env:USERPROFILE\.cache\opencode\models.json"
$env:OPENCODE_DISABLE_DEFAULT_PLUGINS = "true"
$env:OPENCODE_DISABLE_AUTOUPDATE = "true"
$env:OPENCODE_DISABLE_LSP_DOWNLOAD = "true"

Step 4: Resolve Dependency Installation Hang (Critical for Air-gapped ⚠️)

⚠️ Important

This is the most common cause of startup hangs in air-gapped environments! Even with OPENCODE_DISABLE_DEFAULT_PLUGINS=true, OpenCode still attempts to install the @opencode-ai/plugin SDK, which is not controlled by any environment variable.

Root cause: The installation logic in src/config/config.ts:237-257 executes bun add and bun install, which hangs in air-gapped environments.

Solution (Recommended - Option 1):

bash
# Create an empty node_modules directory to skip installation checks
mkdir -p ~/.config/opencode/node_modules

# Verify the directory was created
ls -la ~/.config/opencode/
# You should see the node_modules directory

💡 Alternative Solutions

If Option 1 doesn't work for you:

  • Option 2: Configure an internal npm mirror (~/.bunfig.toml or .npmrc)
  • Option 3: Pre-install dependencies on an internet-connected machine, then copy ~/.config/opencode/ (and project .opencode/) to the air-gapped machine
  • Option 4: Temporarily disable project config scanning: OPENCODE_DISABLE_PROJECT_CONFIG=true

See "Pitfalls → Dependency Installation Hang (@opencode-ai/plugin)" below for details.

Step 5: Configure Internal AI Gateway

Why
After disabling external network access, you need to tell OpenCode which internal models to use.

Create or edit ~/.config/opencode/opencode.json:

jsonc
{
  "$schema": "https://opencode.ai/config.json",
  
  // Disable auto-update (belt and suspenders, env var already set)
  "autoupdate": false,
  
  // Only enable your internal provider
  "enabled_providers": ["corp-gateway"],
  
  // Configure internal AI gateway
  "provider": {
    "corp-gateway": {
      "name": "Corporate AI Gateway",
      "env": ["CORP_AI_TOKEN"],
      "api": "https://ai-gateway.company.internal/v1",
      "npm": "@ai-sdk/openai-compatible",
      "models": {
        "qwen2.5-72b": {
          "name": "Qwen 2.5 72B",
          "tool_call": true,
          "reasoning": true,
          "temperature": true,
          "limit": { "context": 128000, "output": 8192 }
        },
        "glm-4": {
          "name": "GLM-4",
          "tool_call": true,
          "temperature": true,
          "limit": { "context": 128000, "output": 4096 }
        }
      }
    }
  },
  
  // Default to internal model
  "model": "corp-gateway/qwen2.5-72b"
}

💡 Using Local Ollama

Change api to http://localhost:11434/v1, see 1.4g Ollama Configuration.

Step 6: Set API Token

Why
Internal gateways typically require authentication.

bash
export CORP_AI_TOKEN="your-internal-token"

Step 7: Verify Configuration

Why
Ensure all configurations are effective and OpenCode can start normally.

bash
opencode run -m corp-gateway/qwen2.5-72b "1+1=?" --print-logs

Expected result:

  • No network timeout errors
  • Model returns result correctly (e.g., 2)

If it still hangs, add --log-level DEBUG for detailed logs:

bash
opencode run -m corp-gateway/qwen2.5-72b "1+1=?" --print-logs --log-level DEBUG

Checklist ✅

All items must pass to continue; if any fail, return to the corresponding step

  • [ ] A working internal AI gateway (or local Ollama)
  • [ ] Ability to download https://models.dev/api.json from an internet-connected machine
  • [ ] Familiarity with 1.4 Connect Models basic configuration
  • [ ] Model list ready: ~/.cache/opencode/models.json exists and OPENCODE_MODELS_PATH points to it (or using internal mirror OPENCODE_MODELS_URL)
  • [ ] Environment variables set (check with env | grep OPENCODE)
  • [ ] Created ~/.config/opencode/node_modules (also create ./.opencode/node_modules if current project has .opencode/)
  • [ ] opencode.json configured with internal provider
  • [ ] opencode run returns results without network errors
  • [ ] (Optional) ripgrep installed (rg --version outputs version)

Pitfalls

SymptomPossible CauseSolution
Startup hangs, no more logsDependency installation hanging in air-gapped environment (@opencode-ai/plugin)See "Dependency Installation Hang (@opencode-ai/plugin)"
"model not found" errormodels.json path or content incorrectCheck if OPENCODE_MODELS_PATH points to correct file
"provider not found" errorenabled_providers doesn't match provider idCheck enabled_providers, provider key, and model prefix
Model call returns 401/403Token not injected or expiredCheck if CORP_AI_TOKEN is correct and exported
grep tool failsripgrep not installed and cannot downloadManually install rg (see "grep tool special case")

Dependency Installation Hang (@opencode-ai/plugin)

Why It Hangs

When scanning configuration directories, OpenCode triggers dependency installation (source: src/config/config.ts:157-159 and src/config/config.ts:237-257). It executes:

bash
bun add @opencode-ai/plugin@<version> --exact
bun install

If node_modules/ doesn't exist in a directory, OpenCode waits for installation to complete before continuing; in air-gapped environments without npm registry access, this appears as "hanging".

First, Confirm This Is the Issue

Run with DEBUG logging:

bash
opencode run "test" --print-logs --log-level DEBUG

If you see cmd=[..., "add", "@opencode-ai/plugin@..."] in logs with service=bun, it's likely the dependency installation hanging.

Quick Fix (Stop Waiting for Installation)

Add node_modules/ to all directories OpenCode might scan:

bash
# Global config directory
mkdir -p ~/.config/opencode/node_modules

# If running in a project with .opencode/, add one there too
mkdir -p ./.opencode/node_modules

This prevents OpenCode from await installDependencies(...) during startup.

⚠️ Not a Complete Solution

installDependencies(dir) is still triggered (just won't block startup). If you need actual installation, you still need an internal npm mirror or pre-installed dependencies.

Long-term Solutions (Recommended)

  1. Configure internal npm mirror (Bun uses your config)

~/.bunfig.toml:

toml
[install]
registry = "http://your-internal-npm-registry/"
  1. Pre-install and copy dependencies

On an internet-connected machine, let ~/.config/opencode/ and project .opencode/ complete dependency installation, then copy both directories to the air-gapped machine.

  1. Temporarily disable project config scanning

If you don't need project-level .opencode/ (using only global config):

bash
export OPENCODE_DISABLE_PROJECT_CONFIG=true

grep Tool Special Case

Symptom: AI's grep tool fails, complaining about missing rg binary.

Cause: The grep tool depends on ripgrep (rg). If rg isn't in the system, OpenCode tries to download it from GitHub (fails in air-gapped environments).

Solution:

bash
# macOS
brew install ripgrep

# Linux
apt install ripgrep  # Ubuntu/Debian
yum install ripgrep  # CentOS/RHEL

# Windows
scoop install ripgrep
# or
choco install ripgrep

rg --version

How to Verify Environment Variables Are Set?

bash
env | grep OPENCODE

How to Identify External Network Requests?

bash
opencode run "test" --print-logs --log-level DEBUG

Common keywords:

  • service=models.dev: Fetching model list
  • service=bun: Executing bun add/install (installing dependencies or plugins)

Configuration Quick Reference

Environment Variables

VariablePurposeValue
OPENCODE_DISABLE_MODELS_FETCH(Fully offline mode) Disable model list fetchingtrue
OPENCODE_MODELS_PATH(Fully offline mode) Local model list file pathAbsolute file path
OPENCODE_MODELS_URL(Internal mirror mode) Point models.dev to internal address (must provide /api.json)https://models-mirror.company.internal
OPENCODE_DISABLE_DEFAULT_PLUGINSDisable built-in plugin installationtrue
OPENCODE_DISABLE_AUTOUPDATEDisable auto-update checktrue
OPENCODE_DISABLE_LSP_DOWNLOADDisable LSP server downloadstrue
OPENCODE_DISABLE_PROJECT_CONFIG(Optional) Disable project-level .opencode/ scanningtrue

💡 Priority Notes

  • If both OPENCODE_MODELS_PATH and OPENCODE_MODELS_URL are set, OPENCODE_MODELS_PATH takes priority
  • For "internal mirror mode", do NOT set OPENCODE_DISABLE_MODELS_FETCH=true, otherwise fetching won't occur

One-click Setup Scripts

Basic Script (Fully Offline: Manually download models.json):

bash
#!/bin/bash
# save as: setup-airgapped.sh

# Create directories
mkdir -p ~/.cache/opencode

# Check model list file
if [ ! -f ~/.cache/opencode/models.json ]; then
    echo "❌ Please download models.json to ~/.cache/opencode/models.json first"
    exit 1
fi

# Resolve SDK installation hang (critical step!)
mkdir -p ~/.config/opencode/node_modules
echo "✅ Created ~/.config/opencode/node_modules directory"

# If current directory has .opencode/, add one there too
if [ -d ./.opencode ]; then
  mkdir -p ./.opencode/node_modules
  echo "✅ Created ./.opencode/node_modules directory"
fi

# Add environment variables to shell config
SHELL_RC="$HOME/.$(basename $SHELL)rc"
cat >> "$SHELL_RC" << 'EOF'

# OpenCode air-gapped configuration
export OPENCODE_DISABLE_MODELS_FETCH=true
export OPENCODE_MODELS_PATH=~/.cache/opencode/models.json
export OPENCODE_DISABLE_DEFAULT_PLUGINS=true
export OPENCODE_DISABLE_AUTOUPDATE=true
export OPENCODE_DISABLE_LSP_DOWNLOAD=true
EOF

echo "✅ Environment variables added to $SHELL_RC"
echo "Please run: source $SHELL_RC"

Advanced Script (Internal Mirror: Auto-fetch model list):

bash
#!/bin/bash
# save as: setup-airgapped-advanced.sh

# Company internal models.dev server
MODELS_MIRROR="https://models-mirror.company.internal"

# Add environment variables to shell config
SHELL_RC="$HOME/.$(basename $SHELL)rc"
cat >> "$SHELL_RC" << EOF

# OpenCode air-gapped configuration (using internal mirror)
unset OPENCODE_DISABLE_MODELS_FETCH
export OPENCODE_MODELS_URL="$MODELS_MIRROR"
export OPENCODE_DISABLE_DEFAULT_PLUGINS=true
export OPENCODE_DISABLE_AUTOUPDATE=true
export OPENCODE_DISABLE_LSP_DOWNLOAD=true
EOF

# Resolve SDK installation hang
mkdir -p ~/.config/opencode/node_modules
echo "✅ Created ~/.config/opencode/node_modules directory"

echo "✅ Environment variables added to $SHELL_RC"
echo "Configured internal models.dev mirror: $MODELS_MIRROR"
echo "Please run: source $SHELL_RC"

💡 Model List Options Comparison

OptionProsCons
OPENCODE_MODELS_PATHFully offline, version controlledManual models.json updates required
OPENCODE_MODELS_URLAuto-updates, multi-machine syncDepends on internal mirror server

Lesson Summary

You learned:

  1. 5 Key Switches: Fully offline (OPENCODE_DISABLE_MODELS_FETCH + OPENCODE_MODELS_PATH) or internal mirror (OPENCODE_MODELS_URL), plus disable built-in plugins/auto-update/LSP download
  2. Local Model List: Download models.json from internet, transfer to air-gapped machine (or use OPENCODE_MODELS_URL to point to internal mirror)
  3. Internal Provider Configuration: Use enabled_providers to enable only internal gateways
  4. Critical SDK Installation Issue: Solve by creating empty node_modules directory or other solutions
  5. Troubleshooting Techniques: Use --print-logs --log-level DEBUG to identify where it hangs

💡 Additional Tip

If your company has an internal models.dev server, you can use OPENCODE_MODELS_URL instead of OPENCODE_MODELS_PATH:

bash
export OPENCODE_MODELS_URL=https://models-mirror.company.internal

This way OpenCode fetches the model list from your internal server without manual models.json download.


Next Lesson

If you want to take it further with centralized authentication management (so your team doesn't each need to configure tokens), check out 5.11a Enterprise Authentication Integration.

It introduces the /.well-known/opencode mechanism for "one-command login + automatic organization config delivery".


Appendix: Source Code Reference

Click to expand source code locations

Last updated: 2026-02-05

FeatureFile PathLine Numbers
Environment variable definitions (all OPENCODE_* flags)src/flag/flag.ts12-50
Model list loading logic (OPENCODE_DISABLE_MODELS_FETCH, OPENCODE_MODELS_PATH, OPENCODE_MODELS_URL)src/provider/models.ts83-99
Config directory scanning and dependency installation waiting (node_modules check)src/config/config.ts157-160
Dependency installation commands (bun add + bun install)src/config/config.ts236-257
Plugin loading (built-in plugin list, OPENCODE_DISABLE_DEFAULT_PLUGINS check)src/plugin/index.ts18-49
Bun package installation logic (may cause startup hang)src/bun/index.ts64-133
ripgrep download logic (often fails in air-gapped environments)src/file/ripgrep.ts125-199
Auto-update check (OPENCODE_DISABLE_AUTOUPDATE)src/cli/upgrade.ts6-25
LSP server download check (OPENCODE_DISABLE_LSP_DOWNLOAD)src/lsp/server.ts135, 177, 372...

Key Constants:

  • BUILTIN = ["[email protected]", "@gitlab/[email protected]"]: Built-in plugin list
  • Flag.OPENCODE_DISABLE_MODELS_FETCH: Disable model list fetching
  • Flag.OPENCODE_MODELS_PATH: Local model list file path
  • Flag.OPENCODE_MODELS_URL: Internal models.dev server URL
  • Flag.OPENCODE_DISABLE_DEFAULT_PLUGINS: Disable built-in plugin installation

Network Request Priority:

  1. OPENCODE_MODELS_PATH: If set, takes priority over OPENCODE_MODELS_URL
  2. OPENCODE_MODELS_URL: Points to internal models.dev server
  3. https://models.dev: Default address (when above not set and OPENCODE_DISABLE_MODELS_FETCH=false)