OpenCode'u konteyner ortamında çalıştırma

2026-05-30 Doğukan Meral

Bu yazıda Podman kullanarak, OpenCode’u ana sistemden izole (konteynerize) bir şekilde nasıl kullanabileceğimizi anlatmaya çalışacağım. Bu izole ortama, komut satırından kolayca erişmek için yazdığım Bash betiğinden de bahsedeceğim.

Büyük dil modelleri tabanlı sohbet botları, günümüzde farklı farklı kılıklarda internetin birçok yerinde karşımıza çıkabiliyor.

Sohbet botlarının oldukça faydalı olabileceği yerlerden biri ise; yazılım mühendislerinin, sistem yöneticilerinin ve bu yola gönül vermiş birçok insanın başında uzun saatler geçirdiği: Terminaller (terminal emülatorleri diye okuyun siz)


Containerfile ve imaj oluşumu

Containerfile:

FROM docker.io/node:22-bookworm

RUN mkdir --parents /root/.npm-global \
 && npm config set prefix /root/.npm-global \
 && npm config set ignore-scripts false \
 && npm install --global opencode-ai --loglevel verbose \
 && cd /root/.npm-global/lib/node_modules/opencode-ai \
 && node postinstall.mjs

ENTRYPOINT ["/root/.npm-global/bin/opencode"]

NodeJS 22 Debian Bookworm imajının üstüne /root/.npm-global dizinine, opencode-ai NPM paketini kurulacak şekilde ve giriş noktasını (konteyner başlağında çalışacak işlem) /root/.npm-global/bin/opencode yoluna kurulmuş, opencode çalıştırılabilir dosyası olarak ayarlayalım.

Containerfile’ı kullanarak, opencode etiketiyle bir imaj oluşturalım:

$ podman image build --tag opencode --file Containerfile

Kolay erişim için Bash betiği

Betikte yerel dizinler konteynerin dosya sistemine bağlandığı için, konteynerize OpenCode ile ana sisteme kurulu OpenCode birlikte kullanılabilir! Yapılandırmalar, ajanlar, oturumlar ve diğer tüm program verileri bu iki kurulum arasında paylaşılır.

copencode:

#!/bin/bash

set -e

IMAGE_TAG='localhost/opencode-cli'

# Konteynerize dosya sisteminde
OPENCODE_PATH='/root/.npm-global/bin/opencode'

# Ana dosya sistemindeki dizinler
LOCAL_SHARE_DIR="${HOME}/.local/share/opencode"
LOCAL_STATE_DIR="${HOME}/.local/state/opencode"
CONFIG_DIR="${HOME}/.config/opencode"
CACHE_DIR="${HOME}/.cache/opencode"

POSITIONAL_ARGS=()

while [[ $# -gt 0 ]]; do
    case $1 in
        -d|--directory)
            MOUNT_DIR_HOST="$2"
            shift # Pass argument
            shift # Pass value
            ;;
        -o|--mount-options)
            MOUNT_OPTS="$2"
            shift # Pass argument
            shift # Pass value
            ;;
        *)
            POSITIONAL_ARGS+=("$1") # Save arguments which will be passed to copilot
            shift # Pass argument
            ;;
    esac
done

set -- "${POSITIONAL_ARGS[@]}" # Replace the arguments


if [ ! -z "${MOUNT_DIR_HOST}" ]; then
    WORKSPACE_VOLUME_ARG="--volume ${MOUNT_DIR_HOST}:${MOUNT_DIR_CONTAINER}"
    WORKDIR="${MOUNT_DIR_CONTAINER}"
else
    WORKDIR="/root"
fi

if [ ! -z "${MOUNT_OPTS}" ]; then
    WORKSPACE_VOLUME_ARG="${WORKSPACE_VOLUME_ARG}:${MOUNT_OPTS}"
fi

if ! podman volume exists "${DATA_VOLUME}"; then
    podman volume create "${DATA_VOLUME}" # If volume does not exist, create
fi

if ! podman image exists "${IMAGE_TAG}"; then
    printf "Error: Image is not available: %s\n" "${IMAGE_TAG}" # If image does not exist, print error message and exit
    exit 1
fi

for DIR in \
    "${LOCAL_SHARE_DIR}" \
    "${LOCAL_STATE_DIR}" \
    "${CONFIG_DIR}" \
    "${CACHE_DIR}"; do
    if [ ! -d "${DIR}" ]; then
        mkdir --parents "${DIR}" # If directory does not exist, create
    fi
done

podman run \
    --rm \
    --interactive \
    --tty \
    --entrypoint "${OPENCODE_PATH}" \
    ${WORKSPACE_VOLUME_ARG} \
    --volume "${LOCAL_SHARE_DIR}:/root/.local/share/opencode" \
    --volume "${LOCAL_STATE_DIR}:/root/.local/state/opencode" \
    --volume "${CONFIG_DIR}:/root/.config/opencode" \
    --volume "${CACHE_DIR}:/root/.cache/opencode" \
    --workdir "${WORKDIR}" \
    "${IMAGE_TAG}" \
    "$@"

Bash betiğinin genel amacı:

  • -d|--directory ve -o|--mount-options argümanları ile, ana dosya sisteminden konteyner içindeki dosya sistemine bağlanacak dizinin seçilmesi ve bağlama seçeneklerinin ayarlanması
    • Geriye kalan argümanların opencode programına gönderilmesi
  • /root dizininin (NPM dosyaları ve OpenCode yapılandırma dosyaları) kalıcı depolama birimine (volume) bağlanması (yoksa oluşturulması)
  • Konteyner içi çalışma dizininin belirlenmesi (eğer ana dosya sisteminden dizin bağlanmışsa, çalışma dizini orası olarak ayarlanır)
  • Ana dosya sisteminde yoksa OpenCode dizinlerinin oluşturulması
  • podman run komutunun yeni konteyneri durduktan sonra silinecek şekilde, interaktif, /root dizini depolama birimine bağlı ve opencode‘a gönderilen argümanlarla başlatması

Red Hat Enterprise Linux dağıtımıyla beraber gelen, Lightspeed destekli, komut satırı asistanına (command-line-assistant) benzer bir deneyim için, .bashrc dosyanıza bu fonksiyonu ekleyebilirsiniz.

# copencode
c() {
	copencode run "$@" | glow
}

Markdown biçimindeki çıktıyı formatlamak için kullanılan glow aracını dağıtımınızın depolarından kurabilirsiniz.


Kullanımı

Bash betiği dosyasına gerekli çalıştırma yetkilerini verdikten ve PATH ortam değişkenindeki bir dizine taşıdıktan/kopyaladıktan (ben $HOME/.local/bin/ dizinine kopyaladım) sonra aşağıdaki şekillerde kullanabilirsiniz:

  • $ copencode: OpenCode terminal arayüzünü (TUI) aç. Ajan seçimi, LLM servisi bağlantısı, AGENTS.md ayarları vb. şeyler burada halledilir.
  • $ copencode --directory ./test-dosyalari: Ana dosya sistemindeki test-dosyalari dizinini konteynere bağlar ve OpenCode’un çalışma alanı burası olur. (kısaltması -d)
  • $ copencode --directory ./test-dosyalari --mount-options ro: test-dosyalari dizinini konteynere sadece okunabilir olarak bağlar.
  • $ c 'podmanle imajları tar arşivine kaydetme komutu': OpenCode, prompt cevabını kendi terminal arayüzünde göstermek yerine, standart çıktıya yazar. glow aracı çıktıyı formatlar ve cici gösterir.

opencode yukarıdaki örneklere ek; birçok seçeneğe, alt komuta ve kullanım alanına sahiptir. Yazının ve betiğin ana konusu konteyner ile izolasyon olduğundan, dosya sistemi ile ilgili komutlara yoğunlaştım. Detaylı bilgi ve daha çok örnek için: $ opencode --help veya opencode.ai/docs


dogumer@fedora:~ 🚢🐳 $ c "podman niye var ve docker'dan farkı ne?"
                                                                    
                                                                              
  │ build · deepseek-v4-flash                                                 
  │                                                                       
  │ Podman, Docker'ın daemon'sız ve root'suz çalışma felsefesine alternatif   
  │ olarak Red Hat tarafından geliştirildi.                                   
                                                                              
  Ana farklar:                                                                
                                                                              
  • Daemon yok: Docker, Docker Engine adında sürekli çalışan bir daemon       
  gerektirir. Podman'da bu yok — her komut kendi işlemini çalıştırır (fork-   
  exec).                                                                      
  • Root'suz: Podman'ı root olmadan, kendi kullanıcı namespace'inde           
  çalıştırabilirsin. Docker'da (rootless mode hariç) çoğunlukla root gerekir. 
  • Pod desteği: Podman, Kubernetes'teki "pod" konseptini doğal destekler.    
  Docker'da bu yok.                                                           
  • Systemd entegrasyonu: Podman, servisleri systemd ile kolayca yönetilebilir
  şekilde üretir.                                                             
  • Kubernetes uyumluluğu: Podman'da kendi YAML'ın üretebilirsin ( podman     
  generate kube ), Docker'da bu daha zor.                                     
                                                                              
  Neden var? Güvenlik (daemon'suz + root'suz), daha az sistem kaynağı, ve     
  Docker'ın ticari yönüne (Docker Hub kotaları, lisanslama) bağımlı kalmamak. 
  Çoğu Docker komutu Podman'da aynı sözdizimiyle çalışır, o yüzden geçiş      
  kolaydır.                                                                   
                                                                              
  Özet: Docker tek büyük süreç (daemon) + root; Podman süreç başına izolasyon 
  + kullanıcı alanında çalışır.                                               

dogumer@fedora:~/Experiments 🚢🐳 $ ls asciidoc-experiments/
./  ../  aside.adoc  counter.out  counter.py  demo.adoc  demo.pdf  sample.csv  sample.json
dogumer@fedora:~/Experiments 🚢🐳 $ copencode -d asciidoc-experiments/ -o ro

OpenCode izinlerini ayarlama

OpenCode, belirli bir aksiyon almak için aşağıdaki şekillerde yapılandırılabilir:

  • "allow": Sormadan direk aksiyon al
  • "ask": Aksiyon almadan önce kullanıcıya sor
  • "deny": Aksiyonu engelle

Yapılandırma ayarları JSON formatında, .config/opencode/opencode.jsonc dosyasına yazılır:

dogumer@fedora:~/ 🚢🐳 $ vim ~/.config/opencode/opencode.jsonc

{
  "$schema": "https://opencode.ai/config.json",
  "permission": {
    "*": "ask",
    "webfetch": "allow",
    "websearch": "allow",
    "read": {
      "/workspace/*": "allow",
      ".env*": "deny"
    }
  }
}

Benim kullandığım yapılandırmada, varsayılan olarak OpenCode herhangi bir aksiyon almadan soruyor; web erişimine (webfetch ve websearch) sormadan izin veriliyor, /workspace/ dizinindeki (bağlanan dizin) dosyaları sormadan okumasına izin veriliyor ve .env dosyalarını okuması engelleniyor. Kendi kullandığınız ortama ve kişisel tercihlerinize göre izinleri gevşetip sıkılaştırabilirsiniz.

Yapılandırmalar hakkında daha detaylı bilgi için
İzinler hakkında daha detaylı bilgi için