Skip to content

Instantly share code, notes, and snippets.

@OmerFarukOruc
Last active March 7, 2026 08:30
Show Gist options
  • Select an option

  • Save OmerFarukOruc/d1bf2bb1f6f4f01c3a0e63288ada7c94 to your computer and use it in GitHub Desktop.

Select an option

Save OmerFarukOruc/d1bf2bb1f6f4f01c3a0e63288ada7c94 to your computer and use it in GitHub Desktop.
oc-cleanup — Find and kill orphaned OpenCode processes on Linux (workaround for issue #15348)
#!/usr/bin/env bash
# oc-cleanup — Find and kill orphaned opencode processes (no TTY)
# Usage:
# oc-cleanup → list orphans only (dry run)
# oc-cleanup kill → kill them all
# oc-cleanup watch → show live opencode resource usage
# oc-cleanup guard → continuous watchdog (default: 1024MB threshold, 60s interval)
# oc-cleanup guard 512 → custom RAM threshold in MB
# oc-cleanup guard 512 30 → custom threshold + check interval in seconds
set -euo pipefail
export LC_NUMERIC=C
# Colors
BOLD='\033[1m'
DIM='\033[2m'
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
MAGENTA='\033[0;35m'
WHITE='\033[1;37m'
NC='\033[0m'
# Icons
ICON_SKULL="💀"
ICON_CHECK="✓"
ICON_WARN="⚠"
ICON_EYE="👁"
ICON_TRASH="🗑"
ICON_ZAP="⚡"
ICON_SHIELD="🛡"
bar() {
local pct=${1:-0}
local width=15
local filled=$(awk "BEGIN{printf \"%d\", $pct/100*$width}")
local empty=$((width - filled))
local color="$GREEN"
(( $(awk "BEGIN{print ($pct > 70)}") )) && color="$YELLOW"
(( $(awk "BEGIN{print ($pct > 90)}") )) && color="$RED"
printf "${color}"
printf '%0.s█' $(seq 1 $filled 2>/dev/null) || true
printf "${DIM}"
printf '%0.s░' $(seq 1 $empty 2>/dev/null) || true
printf "${NC}"
}
header() {
local title="$1"
local width=58
echo ""
printf " ${CYAN}╭"
printf '─%.0s' $(seq 1 $width)
printf "╮${NC}\n"
printf " ${CYAN}│${NC} ${BOLD}${WHITE}%-${width}s${NC}${CYAN}│${NC}\n" "$title"
printf " ${CYAN}├"
printf '─%.0s' $(seq 1 $width)
printf "┤${NC}\n"
}
footer() {
local width=58
printf " ${CYAN}╰"
printf '─%.0s' $(seq 1 $width)
printf "╯${NC}\n"
echo ""
}
get_orphans() {
ps aux | awk '$0 ~ /opencode/ && $7 == "?" && !/grep/ && !/oc-cleanup/ {print $2, $6/1024, $3}'
}
get_total_mem() {
awk '/MemTotal/ {print $2/1024}' /proc/meminfo
}
format_cmd() {
local cmd="$1"
local max=38
if [[ ${#cmd} -gt $max ]]; then
echo "${cmd:0:$((max-1))}…"
else
echo "$cmd"
fi
}
timestamp() {
date '+%Y-%m-%d %H:%M:%S'
}
guard_kill_over_threshold() {
local threshold_mb=$1
local killed=0
local freed=0
while read -r pid mem cpu; do
local over
over=$(awk "BEGIN{print ($mem > $threshold_mb)}")
if [[ "$over" == "1" ]]; then
kill "$pid" 2>/dev/null && {
printf " ${CYAN}│${NC} ${RED}${ICON_SKULL}${NC} [$(timestamp)] PID ${WHITE}%-8s${NC} %5.0f MB ${RED}KILLED${NC} ${CYAN}│${NC}\n" "$pid" "$mem"
freed=$(awk "BEGIN{print $freed + $mem}")
((killed++))
}
fi
done <<< "$1"
# Re-read orphans inside the function — the first arg is threshold
echo "$killed $freed"
}
case "${1:-}" in
kill)
orphans=$(get_orphans)
if [[ -z "$orphans" ]]; then
header "${ICON_CHECK} OpenCode Orphan Cleanup"
printf " ${CYAN}│${NC} ${GREEN}${ICON_CHECK} No orphaned processes found. All clean!${NC} ${CYAN}│${NC}\n"
footer
exit 0
fi
header "${ICON_TRASH} Killing Orphaned Processes"
count=0
freed=0
while read -r pid mem cpu; do
kill "$pid" 2>/dev/null && {
printf " ${CYAN}│${NC} ${RED}${ICON_SKULL}${NC} PID ${WHITE}%-8s${NC} ${DIM}│${NC} %6.0f MB ${DIM}│${NC} %5.1f%% CPU ${CYAN}│${NC}\n" "$pid" "$mem" "$cpu"
freed=$(awk "BEGIN{print $freed + $mem}")
((count++))
}
done <<< "$orphans"
printf " ${CYAN}├"
printf '─%.0s' $(seq 1 58)
printf "┤${NC}\n"
printf " ${CYAN}│${NC} ${GREEN}${ICON_ZAP} Killed ${WHITE}$count${NC}${GREEN} orphans — freed ${WHITE}~${freed%.*} MB${NC}${GREEN} of RAM${NC} ${CYAN}│${NC}\n"
footer
;;
watch)
total_mem=$(get_total_mem)
header "${ICON_EYE} OpenCode Process Monitor"
printf " ${CYAN}│${NC} ${DIM}%-8s %-8s %-7s %-5s %-28s${NC} ${CYAN}│${NC}\n" "PID" "RAM" "CPU" "TTY" "COMMAND"
printf " ${CYAN}│${NC} ${DIM}%-8s %-8s %-7s %-5s %-28s${NC} ${CYAN}│${NC}\n" "──────" "──────" "─────" "───" "──────────────────────────"
total_rss=0
ps aux | awk '$0 ~ /opencode/ && !/grep/ && !/oc-cleanup/ {
printf "%s %s %s %s ", $2, $6/1024, $3, $7
cmd=""
for(i=11;i<=NF;i++) cmd=cmd (i>11?" ":"") $i
print cmd
}' | while read -r pid mem cpu tty cmd; do
short_cmd=$(format_cmd "$cmd")
mem_pct=$(awk "BEGIN{printf \"%.1f\", $mem/$total_mem*100}")
if [[ "$tty" == "?" ]]; then
tty_display="${RED}${ICON_WARN}${NC}"
status="${RED}orphan${NC}"
else
tty_display="${GREEN}${tty}${NC}"
status=""
fi
printf " ${CYAN}│${NC} ${WHITE}%-8s${NC} %5.0f MB %5.1f%% %-5s %-28s ${CYAN}│${NC}\n" \
"$pid" "$mem" "$cpu" "$tty" "$short_cmd"
done
footer
;;
guard)
threshold_mb=${2:-1024}
interval=${3:-60}
total_mem=$(get_total_mem)
total_kills=0
total_freed=0
header "${ICON_SHIELD} OpenCode Guard Mode"
printf " ${CYAN}│${NC} ${WHITE}RAM threshold:${NC} ${YELLOW}%s MB${NC} per process ${CYAN}│${NC}\n" "$threshold_mb"
printf " ${CYAN}│${NC} ${WHITE}Check interval:${NC} ${YELLOW}%ss${NC} ${CYAN}│${NC}\n" "$interval"
printf " ${CYAN}│${NC} ${WHITE}System RAM:${NC} ${DIM}%.0f MB${NC} ${CYAN}│${NC}\n" "$total_mem"
printf " ${CYAN}│${NC} ${DIM}Press Ctrl+C to stop${NC} ${CYAN}│${NC}\n"
printf " ${CYAN}├"
printf '─%.0s' $(seq 1 58)
printf "┤${NC}\n"
trap 'printf "\n ${CYAN}├"; printf "─%.0s" $(seq 1 58); printf "┤${NC}\n"; printf " ${CYAN}│${NC} ${GREEN}${ICON_SHIELD} Guard stopped.${NC} Killed ${WHITE}%d${NC} total, freed ${WHITE}~%s MB${NC} ${CYAN}│${NC}\n" "$total_kills" "${total_freed%.*}"; footer; exit 0' INT TERM
while true; do
orphans=$(get_orphans)
if [[ -n "$orphans" ]]; then
cycle_kills=0
cycle_freed=0
while read -r pid mem cpu; do
over=$(awk "BEGIN{print ($mem > $threshold_mb)}")
if [[ "$over" == "1" ]]; then
kill "$pid" 2>/dev/null && {
printf " ${CYAN}│${NC} ${RED}${ICON_SKULL}${NC} [$(timestamp)] PID ${WHITE}%-7s${NC} %5.0f MB ${RED}KILLED${NC} ${CYAN}│${NC}\n" "$pid" "$mem"
cycle_freed=$(awk "BEGIN{print $cycle_freed + $mem}")
((cycle_kills++))
((total_kills++))
total_freed=$(awk "BEGIN{print $total_freed + $mem}")
}
else
printf " ${CYAN}│${NC} ${DIM}${ICON_EYE} [$(timestamp)] PID %-7s %5.0f MB ok${NC} ${CYAN}│${NC}\n" "$pid" "$mem"
fi
done <<< "$orphans"
if [[ "$cycle_kills" -gt 0 ]]; then
printf " ${CYAN}│${NC} ${GREEN}${ICON_ZAP} Cycle: killed ${WHITE}%d${NC}${GREEN}, freed ${WHITE}~%s MB${NC}${GREEN} (total: %d)${NC} ${CYAN}│${NC}\n" \
"$cycle_kills" "${cycle_freed%.*}" "$total_kills"
fi
else
printf " ${CYAN}│${NC} ${DIM}${ICON_CHECK} [$(timestamp)] No orphans${NC} ${CYAN}│${NC}\n"
fi
sleep "$interval"
done
;;
*)
orphans=$(get_orphans)
if [[ -z "$orphans" ]]; then
header "${ICON_CHECK} OpenCode Orphan Scanner"
printf " ${CYAN}│${NC} ${GREEN}${ICON_CHECK} No orphaned processes found. All clean!${NC} ${CYAN}│${NC}\n"
footer
exit 0
fi
total_mem=$(get_total_mem)
header "${ICON_WARN} Orphaned OpenCode Processes"
printf " ${CYAN}│${NC} ${DIM}%-10s %8s %7s %-17s${NC} ${CYAN}│${NC}\n" "PID" "RAM" "CPU" "RAM USAGE"
printf " ${CYAN}│${NC} ${DIM}%-10s %8s %7s %-17s${NC} ${CYAN}│${NC}\n" "────────" "──────" "─────" "───────────────"
total=0
while read -r pid mem cpu; do
mem_pct=$(awk "BEGIN{printf \"%.1f\", $mem/$total_mem*100}")
usage_bar=$(bar "$mem_pct")
printf " ${CYAN}│${NC} ${RED}${ICON_SKULL}${NC} ${WHITE}%-7s${NC} %5.0f MB %5.1f%% %s %5.1f%% ${CYAN}│${NC}\n" \
"$pid" "$mem" "$cpu" "$usage_bar" "$mem_pct"
total=$(awk "BEGIN{print $total + $mem}")
done <<< "$orphans"
total_pct=$(awk "BEGIN{printf \"%.1f\", $total/$total_mem*100}")
printf " ${CYAN}├"
printf '─%.0s' $(seq 1 58)
printf "┤${NC}\n"
printf " ${CYAN}│${NC} ${YELLOW}Total wasted: ${WHITE}${total%.*} MB${NC} ${DIM}(${total_pct}%% of system RAM)${NC} ${CYAN}│${NC}\n"
printf " ${CYAN}│${NC} Run ${WHITE}oc-kill${NC} to free it ${CYAN}│${NC}\n"
footer
;;
esac

oc-cleanup — OpenCode Orphan Process Killer

Workaround for opencode#15348unbounded heap growth + orphaned processes on Linux.

OpenCode processes don't handle SIGHUP, so when you close a terminal they become orphans and keep eating RAM forever. This script finds and kills them.

Install

# Download
curl -fsSL https://gist.githubusercontent.com/OmerFarukOruc/d1bf2bb1f6f4f01c3a0e63288ada7c94/raw/oc-cleanup -o ~/.local/bin/oc-cleanup
chmod +x ~/.local/bin/oc-cleanup

# Add aliases to your shell rc (~/.zshrc or ~/.bashrc)
echo '# OpenCode cleanup aliases
alias oc-orphans="oc-cleanup"
alias oc-kill="oc-cleanup kill"
alias oc-watch="oc-cleanup watch"
alias oc-guard="oc-cleanup guard"' >> ~/.zshrc

source ~/.zshrc

Usage

oc-orphans          # List orphaned opencode processes (no TTY)
oc-kill             # Kill all orphaned opencode processes
oc-watch            # Show all live opencode processes with resource usage
oc-guard            # Continuous watchdog — kill orphans over 1GB RAM (check every 60s)
oc-guard 512        # Custom threshold — kill orphans over 512MB
oc-guard 512 30     # Custom threshold (512MB) + check interval (30s)

What it looks like

oc-orphans

╭──────────────────────────────────────────────────────────╮
│ ⚠  Orphaned OpenCode Processes                          │
├──────────────────────────────────────────────────────────┤
│  💀 2681180    49 MB   0.1%  █░░░░░░░░░░░░░░░   0.2%   │
│  💀 2869680   250 MB   9.4%  █░░░░░░░░░░░░░░░   0.8%   │
├──────────────────────────────────────────────────────────┤
│  Total wasted: 298 MB (1.0% of system RAM)              │
│  Run oc-kill to free it                                 │
╰──────────────────────────────────────────────────────────╯

oc-kill

╭──────────────────────────────────────────────────────────╮
│ 🗑 Killing Orphaned Processes                            │
├──────────────────────────────────────────────────────────┤
│  💀 PID 2681180  │     49 MB │   0.1% CPU               │
│  💀 PID 2869680  │    250 MB │   9.4% CPU               │
├──────────────────────────────────────────────────────────┤
│  ⚡ Killed 2 orphans — freed ~298 MB of RAM              │
╰──────────────────────────────────────────────────────────╯

oc-watch

╭──────────────────────────────────────────────────────────╮
│ 👁  OpenCode Process Monitor                            │
├──────────────────────────────────────────────────────────┤
│  2835012    796 MB  35.5%  pts/2 opencode               │
│  2869680    250 MB   9.4%  ?     opencode -s ses_35f…   │
╰──────────────────────────────────────────────────────────╯

oc-guard

╭──────────────────────────────────────────────────────────╮
│ 🛡  OpenCode Guard Mode                                 │
├──────────────────────────────────────────────────────────┤
│  RAM threshold:  1024 MB per process                    │
│  Check interval: 60s                                    │
│  System RAM:     32068 MB                               │
│  Press Ctrl+C to stop                                   │
├──────────────────────────────────────────────────────────┤
│  ✓ [2025-03-01 14:30:00] No orphans                     │
│  👁 [2025-03-01 14:31:00] PID 2869680   250 MB  ok      │
│  💀 [2025-03-01 14:32:00] PID 2681180  1280 MB KILLED   │
│  ⚡ Cycle: killed 1, freed ~1280 MB (total: 1)           │
├──────────────────────────────────────────────────────────┤
│  🛡 Guard stopped. Killed 1 total, freed ~1280 MB       │
╰──────────────────────────────────────────────────────────╯

Guard mode options

Argument Default Description
$2 — threshold 1024 RAM threshold in MB. Orphans exceeding this get killed.
$3 — interval 60 Check interval in seconds.

Run in the background with nohup:

nohup oc-cleanup guard 512 30 > /tmp/oc-guard.log 2>&1 &

Or as a systemd user service:

mkdir -p ~/.config/systemd/user

cat > ~/.config/systemd/user/oc-guard.service << 'EOF'
[Unit]
Description=OpenCode orphan process guard
After=default.target

[Service]
ExecStart=%h/.local/bin/oc-cleanup guard 1024 60
Restart=on-failure
RestartSec=10
Environment=TERM=dumb

[Install]
WantedBy=default.target
EOF

systemctl --user daemon-reload
systemctl --user enable --now oc-guard

Optional: Auto-cleanup via cron

# Kill orphans every 30 minutes
(crontab -l 2>/dev/null; echo "*/30 * * * * $HOME/.local/bin/oc-cleanup kill >/dev/null 2>&1") | crontab -

Why this exists

OpenCode on Linux has a known bug where:

  1. Heap memory grows unboundedly — reaching 37+ GB in some cases
  2. Processes don't die when you close the terminal (no SIGHUP handler)
  3. Orphaned processes keep consuming RAM/CPU silently

Related issues: #15348, #14504, #13230, #12913, #10563

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment