> ## Content Index
> Fetch the complete content index at: https://mistercyber.lt/llms.txt
> Use this file to discover other available public pages before exploring further.

# My Ultimate OpenCode Vibe-Coding Setup: Local Models, Docker, and a Tauri App
- URL: https://mistercyber.lt/ultimate-opencode-vibe-coding-setup/
- Published: 2026-09-15T23:39:58.000Z
- Updated: 2026-09-15T23:39:58.000Z
- Description: A grounded deep-dive into my vibe-coding rig: Ryzen 7 5700X + RTX 5070 Ti + 32GB on CachyOS, local-first model routing with Qwen2.5-Coder and DeepSeek V4, a Docker home server, and a custom Tauri v2 control app.
- Author: Aurimas
- Tags: AI, Linux, Code, Experiments

Vibe coding gets a bad rap because most people do it wrong. They lean on one giant cloud model, let it blast through a repo, and pray. The result is context soup, $200 bills, and code nobody understands.

This is the opposite: a **local-first, tiered setup** where the machine and the models each do what they're actually good at. The hardware is modest by 2026 standards. The workflow is anything but.

Here's the full breakdown — hardware, model routing, the home server, and the custom Tauri app that ties the control plane together.

## The Hardware: Modest, Balanced, Repairable

- **CPU:** AMD Ryzen 7 5700X (8C / 16T)
- **GPU:** NVIDIA RTX 5070 Ti — **16 GB VRAM** (driver 615.71)
- **RAM:** 32 GB DDR4
- **OS:** CachyOS (Arch-based, `x86_64`)

The RTX 5070 Ti is the quiet workhorse here. 16 GB of VRAM is the sweet spot for local LLMs: you can comfortably run 9–14B models at Q8/Q5, and it handles MoE quants for experiments. CachyOS on the Ryzen gives me a kernel tuned for responsiveness, and every model query is just another process to the OS.

> Rule of thumb I live by: **VRAM decides what runs locally, RAM decides how many things run at once, and the CPU decides how fast the tooling feels.** 32 GB of system RAM matters more than people think when you're running an editor, a Docker stack, *and* a model server at the same time.

## Model Routing: Local-First, Then Cheap Cloud, Then Premium

The whole trick is a **three-tier escalation ladder**. OpenCode's config supports per-role models plus explicit routing tiers, so "vibe coding" here is really just disciplined routing.

### Tier 0 — Local (llama.cpp)

Default everything to local. The `llama-local` provider points at a llama.cpp server on `127.0.0.1:8080`, and the everyday driver is a small coder model (Qwen2.5-Coder class). Instant, private, free.

```jsonc
{
  "model": "llama-local/qwen2.5-coder-7b-q8",
  "small_model": "llama-local/qwen2.5-coder-7b-q8",

  "provider": {
    "llama-local": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "LOCAL — llama.cpp",
      "options": {
        "baseURL": "http://127.0.0.1:8080/v1",
        "timeout": 600000,
        "headerTimeout": 120000,
        "chunkTimeout": 120000
      },
      "models": {
        "qwen2.5-coder-7b-q8": {
          "name": "LOCAL ⚡ Qwen2.5 Coder 7B Q8 — Everyday",
          "tools": true,
          "reasoning": true,
          "limit": { "context": 32768, "output": 8192 }
        }
      }
    }
  }
}

```

Why Q8? Because at 7–14B scale, quantization error shows up as subtle logic drift. Q8 is close enough to the unquantized model that I trust its edits in my own repos. The context limit of 32K is generous for single-file and small-module work.

### Tier 1 — Cheap Cloud (DeepSeek V4)

When the local model is stuck, hop to the cheap cloud worker. DeepSeek V4 Flash is the cost king here — roughly **$0.082/M input, $0.165/M output** with a **1M-token context**. That 1M context is the real superpower: it can swallow an entire codebase in one shot.

```jsonc
"openrouter": {
  "name": "CLOUD — OpenRouter",
  "models": {
    "deepseek/deepseek-v4-flash": {
      "name": "CLOUD 💰 DeepSeek V4 Flash — Cheap Worker",
      "limit": { "context": 1048576, "output": 32768 },
      "cost": { "input": 0.08246, "output": 0.16492 }
    }
  }
}

```

I use the model's declared `cost` fields to let the router pick the cheap path, and I never leave a session on this tier when a local run would do.

### Tier 2 — Premium, Escalation Only

Review, architecture, and the rare "I've been fighting this for an hour" moment get the premium models. These are gated by cost tiers so the default session never drifts into them accidentally:

```jsonc
"auto-low":    { "id": "openrouter/auto", "options": { "plugins": [{ "id": "auto-router", "cost_tier": "low" }] } },
"auto-medium": { "id": "openrouter/auto", "options": { "plugins": [{ "id": "auto-router", "cost_tier": "medium" }] } },
"auto-high":   { "id": "openrouter/auto", "options": { "plugins": [{ "id": "auto-router", "cost_tier": "high" }] } },
"auto-max":    { "id": "openrouter/auto", "options": { "plugins": [{ "id": "auto-router", "cost_tier": "max" }] } }

```

### Context hygiene is the real optimization

The single biggest vibe-coding win is **guarding context**. My OpenCode config keeps agent fan-out shallow, auto-compacts aggressively, and caps tool output — otherwise one `grep` flood eats the whole window and the model goes dumb:

```jsonc
"subagent_depth": 1,
"compaction": {
  "auto": true,
  "prune": true,
  "reserved": 10000,
  "tail_turns": 6,
  "preserve_recent_tokens": 12000
},
"tool_output": {
  "max_lines": 200,
  "max_bytes": 16384
}

```

Small, disciplined context in = correct, fast edits out. This is the difference between "vibe coding" and "garbage generation with extra steps."

## The Docker Home Server

The box doubles as a self-hosted server. Two stacks that actually pull their weight:

**1\. This blog's preview stack** — Ghost 6.63 + MySQL 8.4 behind a health-checked compose file. I develop the theme locally against a real Ghost instance (volume-mounted read-only) before anything touches production:

```yaml
services:
  db:
    image: mysql:8.4
    environment:
      MYSQL_DATABASE: ghost
      MYSQL_USER: ghost
      MYSQL_PASSWORD: local-ghost-db-password
    volumes:
      - db_data:/var/lib/mysql

  ghost:
    image: ghost:6.63.0
    depends_on:
      db:
        condition: service_healthy
    ports:
      - "127.0.0.1:2368:2368"
    volumes:
      - ./content:/var/lib/ghost/content
      - /home/mistercyber/projects/mistercyber-theme:/var/lib/ghost/content/themes/mistercyber-signal:ro

```

The `condition: service_healthy` \+ `start_period: 30s` pattern is non-negotiable — it means Ghost never boots before MySQL is actually answering.

**2\. Godot MCP infra** — a containerized MCP server for the Godot editor, exposed over HTTP/WS on a trusted host. The fun detail: it uses **bridge inversion** (`GODOT_MCP_BRIDGE_URL: ws://0.0.0.0:9080`), so the editor connects *in* to the container instead of the container reaching out. Bearer-token auth is forced when you bind off-loopback, which is the kind of sharp edge that Docker encourages you to get right.

## The Custom Tauri v2 App: "Cachy Control"

The crown piece: a **custom Tauri v2 app** I built to control the machine itself. Tauri v2 apps are tiny (system webview + a Rust core) — no 150 MB Electron blob, no JS runtime bundled with your OS shell.

The window is a sane default: 1280×800, resizable, centered, with a CSP that locks the webview down hard:

```json
{
  "$schema": "https://raw.githubusercontent.com/nickelpack/tauri-v2-docs/main/tauri.conf.schema.json",
  "productName": "Cachy Control",
  "identifier": "com.cachycontrol.app",
  "build": {
    "beforeDevCommand": "pnpm dev",
    "devUrl": "http://localhost:1420",
    "beforeBuildCommand": "pnpm build",
    "frontendDist": "../dist"
  },
  "app": {
    "withGlobalTauri": false,
    "windows": [
      {
        "title": "Cachy Control",
        "width": 1280,
        "height": 800,
        "minWidth": 960,
        "minHeight": 640,
        "resizable": true,
        "center": true
      }
    ],
    "security": {
      "csp": {
        "default-src": "'self' customprotocol: asset:",
        "connect-src": "ipc: http://ipc.localhost",
        "img-src": "'self' asset: http://asset.localhost blob: data:",
        "style-src": "'unsafe-inline' 'self'",
        "script-src": "'self'"
      }
    }
  }
}

```

Notes worth copying:

- `**withGlobalTauri: false**` — no `window.__TAURI__` global leaking into every page. Use the IPC bindings or the `@tauri-apps/api` package instead.
- **Explicit CSP** — `connect-src` only allows the internal IPC channel, `script-src: 'self'`. If your app shell ever becomes an XSS target, this is your last line of defense.
- **`default-src` includes `asset:` and `customprotocol:`** — Tauri v2 needs these for asset/streaming protocols; forgetting them breaks local file access in confusing ways.

The app itself does hardware detection, explains settings in plain language, applies recommended changes, and — the part I'm proudest of — **creates restore points before it touches anything**. Vibe-coding your *operating system* without rollback is how you end up reinstalling at 2 AM. Tauri v2's Rust commands make the boundary explicit: the UI is dumb, the shell has the permissions, and nothing runs unsandboxed.

```rust
// The pattern: safe command wrapper around a system change
#[tauri::command]
fn apply_restore_point(app: AppHandle, settings: Settings) -> Result<(), String> {
    create_restore_point(&settings)?;  // rollback BEFORE mutate
    apply_settings(&settings).map_err(|e| e.to_string())
}

```

The whole vibe-coding workflow is that same `restore-point-before-mutate` philosophy applied to code.

## Putting It Together: The Daily Loop

1. **OpenCode boots local-first** — every keystroke-level request hits Qwen2.5-Coder on the RTX 5070 Ti. Zero latency surprise, zero cost, and my session data never leaves the box.
2. **Larger refactors escalate to DeepSeek V4** — 1M-token context eats the whole repo; cost stays in the pennies.
3. **Reviews and architecture go premium** — cost-tier gated, manually invoked, never the default.
4. **Docker serves the Ghost preview and the Godot MCP infra** while the models run — 32 GB of RAM is what makes this all coexist.
5. **Cachy Control** (Tauri v2) is the system-level "undo button" for any OS experiment the vibe sessions suggest.

## What I'd Tell Someone Rebuilding This

- **Buy VRAM, not hype.** 16 GB runs a genuinely useful local coding model. You do not need a $4K GPU to vibe code well.
- **Routing is the product.** A 7B model used in the right tier beats a 500B model used in the wrong one.
- **Quantize responsibly.** Q8 for anything that writes code you'll keep.
- **Health checks in compose, restore points in Tauri, compacting in OpenCode.** The tools that save you are always the ones that let you *roll back*.
- **Keep context small.** The models get better, but your repo context budget still decays with every irrelevant `rg` dump.

The hardware is ordinary. The config is deliberate. And that's the whole point — vibe coding at its best isn't magic, it's **routing discipline with an undo button**.