diff --git a/README.md b/README.md
index 87f2d5ddd6..9f390227eb 100644
--- a/README.md
+++ b/README.md
@@ -62,6 +62,27 @@ No vendored files in your repo, no version drift, no manual upgrades. Every Clau
Swap `required` for `optional` if you'd rather nudge teammates than block them.
+### Advanced Linux Installation (Arch Linux, NixOS, etc.)
+
+If you are using a non-Ubuntu Linux distribution, Playwright's pre-compiled Chromium binaries may fail to launch due to missing system libraries or sandbox constraints.
+
+You can bypass the bundled browser download and use your system's native Chromium instead by installing it via your package manager and setting the environment variables:
+
+```bash
+# Arch Linux example
+sudo pacman -S chromium
+
+# Export variables before running setup
+export GSTACK_SKIP_PLAYWRIGHT=1
+export GSTACK_CHROMIUM_PATH="/usr/bin/chromium"
+export CHROME_PATH="/usr/bin/chromium"
+
+# Run setup
+./setup
+```
+
+Make sure to add these exports to your `~/.bashrc` or `~/.zshrc` so Claude Code can locate your system browser during runtime execution.
+
### OpenClaw
OpenClaw spawns Claude Code sessions via ACP, so every gstack skill just works
diff --git a/browse/src/browser-manager.ts b/browse/src/browser-manager.ts
index fd906caa31..24d3a50fcc 100644
--- a/browse/src/browser-manager.ts
+++ b/browse/src/browser-manager.ts
@@ -218,7 +218,10 @@ export class BrowserManager {
}
this.browser = await chromium.launch({
+ //If GSTACK_CHROMIUM_PATH exist use executablePath, else undefined
+ executablePath: process.env.GSTACK_CHROMIUM_PATH || process.env.PLAYWRIGHT_CHROMIUM_HEADLESS_SHELL_EXECUTABLE_PATH || undefined,
headless: useHeadless,
+ args: process.env.GSTACK_SKIP_PLAYWRIGHT === '1' ? ['--no-sandbox', '--disable-setuid-sandbox'] : [],
// On Windows, Chromium's sandbox fails when the server is spawned through
// the Bun→Node process chain (GitHub #276). Disable it — local daemon
// browsing user-specified URLs has marginal sandbox benefit.
diff --git a/browse/test/bridge-chromium-e2e.test.ts b/browse/test/bridge-chromium-e2e.test.ts
index 9597221535..f6d0badc0c 100644
--- a/browse/test/bridge-chromium-e2e.test.ts
+++ b/browse/test/bridge-chromium-e2e.test.ts
@@ -124,7 +124,9 @@ describe('bridge-chromium-e2e (codex F3)', () => {
});
httpFixture = await startHttpFixture('
via-bridge
');
browser = await chromium.launch({
+ executablePath: process.env.GSTACK_CHROMIUM_PATH || undefined,
headless: true,
+ args: process.env.GSTACK_SKIP_PLAYWRIGHT === '1' ? ['--no-sandbox', '--disable-setuid-sandbox'] : [],
proxy: { server: `socks5://127.0.0.1:${bridge.port}` },
});
});
diff --git a/setup b/setup
index f812511e4d..69436a7140 100755
--- a/setup
+++ b/setup
@@ -205,12 +205,43 @@ if [ "$INSTALL_CODEX" -eq 1 ]; then
fi
ensure_playwright_browser() {
- if [ "$IS_WINDOWS" -eq 1 ]; then
+ if [ "${GSTACK_SKIP_PLAYWRIGHT:-0}" = "1" ]; then
+ log "Skipping download step. Testing system Chromium instead..."
+ (
+ cd "$SOURCE_GSTACK_DIR"
+
+ # Force the Playwright library to be available locally without downloading Ubuntu browsers
+ PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 bun add playwright --dev
+
+ echo "
+ const { chromium } = require('playwright');
+ (async () => {
+ try {
+ const browser = await chromium.launch({
+ executablePath: '${GSTACK_CHROMIUM_PATH:-/usr/bin/chromium}',
+ args: ['--no-sandbox', '--disable-setuid-sandbox', '--headless']
+ });
+ await browser.close();
+ console.log('Smoke test successfully completed!');
+ process.exit(0);
+ } catch (e) {
+ console.error('Error launching system Chromium:', e);
+ process.exit(1);
+ }
+ })();
+ " > test-chromium.js
+
+ bun test-chromium.js
+ local_exit_code=$?
+ rm -f test-chromium.js
+ return $local_exit_code
+ )
+ elif [ "$IS_WINDOWS" -eq 1 ]; then
# On Windows, Bun can't launch Chromium due to broken pipe handling
# (oven-sh/bun#4253). Use Node.js to verify Chromium works instead.
(
cd "$SOURCE_GSTACK_DIR"
- node -e "const { chromium } = require('playwright'); (async () => { const b = await chromium.launch(); await b.close(); })()" 2>/dev/null
+ node -e "const { chromium } = require('playwright'); (async () => { const b = await chromium.launch(); await b.close(); })()"
)
else
(
@@ -220,6 +251,7 @@ ensure_playwright_browser() {
fi
}
+
# 1. Build browse binary if needed (smart rebuild: stale sources, package.json, lock)
NEEDS_BUILD=0
if [ ! -x "$BROWSE_BIN" ]; then
@@ -322,7 +354,9 @@ if [ "$INSTALL_OPENCODE" -eq 1 ] && [ "$NEEDS_BUILD" -eq 0 ]; then
fi
# 2. Ensure Playwright's Chromium is available
-if ! ensure_playwright_browser; then
+if [ "${GSTACK_SKIP_PLAYWRIGHT:-0}" = "1" ]; then
+ echo "Skipping Playwright browser check (GSTACK_SKIP_PLAYWRIGHT=1)"
+elif ! ensure_playwright_browser; then
echo "Installing Playwright Chromium..."
(
cd "$SOURCE_GSTACK_DIR"