feat(deploy): add dedicated Coolify deployment files and guide

This commit is contained in:
mrbeandev 2026-02-17 12:43:56 +05:30
parent a961a2df87
commit 58e09e2f3b
5 changed files with 730 additions and 3 deletions

326
COOLIFY.md Normal file
View file

@ -0,0 +1,326 @@
# ☁️ Deploying PicoClaw on Coolify
Deploy PicoClaw as a self-hosted AI assistant on [Coolify](https://coolify.io) — the open-source Heroku/Vercel alternative.
> **Looking for the quick 3-step version?** See [README.md → Deploy on Coolify](README.md#%EF%B8%8F-deploy-on-coolify-3-steps)
---
## 📋 Prerequisites
- A Coolify instance (v4+)
- A GitHub account (to fork the repo)
- At least one LLM API key (e.g., [Gemini](https://aistudio.google.com/apikey), [OpenRouter](https://openrouter.ai/keys))
## 🚀 Quick Deploy
### Step 1: Fork the Repository
Fork [mrbeandev/picoclaw](https://github.com/mrbeandev/picoclaw) to your GitHub account.
### Step 2: Create a New Service in Coolify
1. Go to your Coolify dashboard → **Projects** → select or create a project
2. Click **+ New** → **Docker Compose**
3. Connect your forked GitHub repo
4. Set the following:
- **Branch:** `deploy/coolify`
- **Docker Compose File:** `docker-compose-coolify.yml`
- **Base Directory:** `/` (root)
### Step 3: Configure Environment Variables
Go to **Environment Variables** tab and add your keys. See [Configuration](#-configuration) below.
### Step 4: Deploy!
Hit **Deploy** and wait for the build to complete (~30 seconds).
---
## ⚙️ Configuration
PicoClaw on Coolify supports **3 configuration methods**. The entrypoint script checks them in order — **first match wins**.
### Which Method Should I Use?
| | Method 1: JSON Env Var | Method 2: Mounted File | Method 3: Individual Env Vars |
|---|---|---|---|
| **Difficulty** | Medium | Easy | Easiest |
| **Flexibility** | ✅ Full control | ✅ Full control | ⚠️ Limited |
| **Custom providers (Ollama, vLLM)** | ✅ Yes | ✅ Yes | ❌ No |
| **Allow-lists** | ✅ Yes | ✅ Yes | ✅ Yes (comma-separated) |
| **Feishu, DingTalk, QQ, OneBot** | ✅ Yes | ✅ Yes | ❌ No |
| **Custom API base URLs** | ✅ Yes | ✅ Yes | ❌ No |
| **Requires JSON minification** | ⚠️ Yes | ❌ No | ❌ No |
| **Edit without rebuild** | ✅ Redeploy only | ✅ Restart only | ✅ Redeploy only |
| **Pretty-printed JSON** | ❌ Must minify | ✅ Yes | N/A |
---
### Method 1: Full JSON Config (Most Flexible) ⭐
**Best for:** Full control, custom providers (Ollama, vLLM), complex setups.
Paste your **entire** `config.json` as a single environment variable:
| Key | Value |
|-----|-------|
| `PICOCLAW_CONFIG_JSON` | `{"agents":{"defaults":{"provider":"gemini",...}},...}` |
#### Example: Gemini + Telegram + Ollama
```json
{
"agents": {
"defaults": {
"provider": "gemini",
"model": "gemini-2.5-flash-lite",
"max_tokens": 8192,
"temperature": 0.7,
"max_tool_iterations": 20,
"workspace": "~/.picoclaw/workspace",
"restrict_to_workspace": true
}
},
"providers": {
"gemini": {
"api_key": "AIzaSy..."
},
"vllm": {
"api_key": "dummy",
"api_base": "http://your-ollama-server:11434/v1"
},
"openrouter": {
"api_key": "sk-or-..."
}
},
"channels": {
"telegram": {
"enabled": true,
"token": "123456:ABC-DEF...",
"allow_from": ["your_telegram_user_id"]
},
"discord": {
"enabled": false,
"token": "",
"allow_from": []
}
},
"tools": {
"web": {
"duckduckgo": { "enabled": true, "max_results": 5 },
"brave": { "enabled": false, "api_key": "", "max_results": 5 }
},
"firecrawl": { "enabled": false, "api_key": "", "api_base": "https://api.firecrawl.dev/v1" },
"serpapi": { "enabled": false, "api_key": "", "max_results": 10 }
},
"heartbeat": { "enabled": true, "interval": 30 },
"gateway": { "host": "0.0.0.0", "port": 18790 },
"devices": { "enabled": false, "monitor_usb": false }
}
```
#### ⚠️ Important: You MUST minify the JSON!
Coolify environment variables are single-line. You need to compress the JSON into **one line** before pasting.
**🔧 JSON Tools for Minifying:**
| Tool | Type | URL |
|------|------|-----|
| **JSON Minifier** | Web | [jsonformatter.org/json-minify](https://jsonformatter.org/json-minify) |
| **JSON Formatter** | Web | [jsonformatter.curiousconcept.com](https://jsonformatter.curiousconcept.com/) |
| **JSON Editor Online** | Web | [jsoneditoronline.org](https://jsoneditoronline.org/) — edit visually, then copy minified |
| **jq** | CLI | `cat config.json \| jq -c .` — outputs minified JSON |
| **Python** | CLI | `python3 -c "import json,sys;print(json.dumps(json.load(sys.stdin)))" < config.json` |
| **Node.js** | CLI | `node -e "process.stdin.on('data',d=>console.log(JSON.stringify(JSON.parse(d))))"< config.json` |
**Workflow:**
1. Write your config in a pretty-printed JSON editor
2. Validate it (the tools above show errors)
3. Minify / compress to one line
4. Paste the single line as the `PICOCLAW_CONFIG_JSON` value in Coolify
**Example minified output:**
```
{"agents":{"defaults":{"provider":"gemini","model":"gemini-2.5-flash-lite","max_tokens":8192,"temperature":0.7,"max_tool_iterations":20,"workspace":"~/.picoclaw/workspace","restrict_to_workspace":true}},"providers":{"gemini":{"api_key":"AIzaSy..."},"vllm":{"api_key":"dummy","api_base":"http://ollama:11434/v1"}},"channels":{"telegram":{"enabled":true,"token":"123456:ABC-DEF...","allow_from":["123456789"]}},"tools":{"web":{"duckduckgo":{"enabled":true,"max_results":5}}},"heartbeat":{"enabled":true,"interval":30},"gateway":{"host":"0.0.0.0","port":18790},"devices":{"enabled":false,"monitor_usb":false}}
```
---
### Method 2: Mounted Config File
**Best for:** Users who prefer editing a normal file, and want pretty-printed JSON without minification.
Use Coolify's **Storages** feature to mount a config file into the container:
#### Step-by-step:
1. Go to your PicoClaw service in Coolify
2. Click the **Storages** tab
3. Click **+ Add** and configure:
- **Source Path:** Leave empty (Coolify auto-creates it) or set to `/data/coolify/applications/<your-app-uuid>/config.json`
- **Destination Path:** `/config/config.json`
4. Save the storage mount
5. SSH into your Coolify server and create the config file:
```bash
# Find your app's data directory
ls /data/coolify/applications/
# Create the config file (replace <uuid> with your app's UUID)
nano /data/coolify/applications/<uuid>/config.json
```
6. Paste your full config JSON (pretty-printed is fine!) and save
7. **Restart** the service in Coolify (no rebuild needed)
The entrypoint will automatically detect `/config/config.json` and use it.
#### ✅ Advantages
- **Pretty-printed JSON** — no minification needed, easy to read and edit
- **Full control** — same flexibility as Method 1
- **Edit without rebuild** — just edit the file on disk and restart the container
- **Custom providers** — Ollama, vLLM, and any other custom provider work fine
#### ❌ Limitations
- **Requires SSH access** — you need SSH into the Coolify server to create/edit the file
- **No Coolify UI editing** — you can't edit the file content from Coolify's web UI (only set the mount path)
- **File must exist before starting** — if the file doesn't exist, this method is skipped and it falls through to Method 3
- **Not portable** — the config lives on the server's filesystem, not in Coolify's database
---
### Method 3: Individual Environment Variables
**Best for:** Simple setups — just Gemini + one channel, defaults for everything else.
Set these in Coolify's **Environment Variables** tab:
#### Required
| Variable | Description | Example |
|----------|-------------|---------|
| `PICOCLAW_PROVIDERS_GEMINI_API_KEY` | Gemini API key | `AIzaSy...` |
#### Provider Keys (optional)
| Variable | Description |
|----------|-------------|
| `PICOCLAW_PROVIDERS_OPENROUTER_API_KEY` | OpenRouter API key |
| `PICOCLAW_PROVIDERS_OPENAI_API_KEY` | OpenAI API key |
| `PICOCLAW_PROVIDERS_ANTHROPIC_API_KEY` | Anthropic API key |
| `PICOCLAW_PROVIDERS_GROQ_API_KEY` | Groq API key |
| `PICOCLAW_PROVIDERS_MISTRAL_API_KEY` | Mistral API key |
| `PICOCLAW_PROVIDERS_DEEPSEEK_API_KEY` | DeepSeek API key |
#### Channel Config (optional)
| Variable | Description |
|----------|-------------|
| `PICOCLAW_CHANNELS_TELEGRAM_ENABLED` | `true` / `false` |
| `PICOCLAW_CHANNELS_TELEGRAM_TOKEN` | Telegram bot token |
| `PICOCLAW_CHANNELS_TELEGRAM_ALLOW_FROM` | Comma-separated user IDs (e.g., `123,456`) |
| `PICOCLAW_CHANNELS_DISCORD_ENABLED` | `true` / `false` |
| `PICOCLAW_CHANNELS_DISCORD_TOKEN` | Discord bot token |
| `PICOCLAW_CHANNELS_DISCORD_ALLOW_FROM` | Comma-separated user IDs |
#### Model Config (optional)
| Variable | Default | Description |
|----------|---------|-------------|
| `PICOCLAW_AGENTS_DEFAULTS_PROVIDER` | `gemini` | LLM provider name |
| `PICOCLAW_AGENTS_DEFAULTS_MODEL` | `gemini-2.5-flash-lite` | Model name |
| `PICOCLAW_AGENTS_DEFAULTS_MAX_TOKENS` | `8192` | Max output tokens |
| `PICOCLAW_AGENTS_DEFAULTS_TEMPERATURE` | `0.7` | Temperature |
#### Other (optional)
| Variable | Default | Description |
|----------|---------|-------------|
| `TZ` | `UTC` | Timezone (e.g., `Asia/Kolkata` for IST) |
| `PICOCLAW_HEARTBEAT_ENABLED` | `true` | Enable heartbeat |
| `PICOCLAW_HEARTBEAT_INTERVAL` | `30` | Heartbeat interval (minutes) |
| `PICOCLAW_TOOLS_WEB_DUCKDUCKGO_ENABLED` | `true` | DuckDuckGo search |
| `PICOCLAW_TOOLS_WEB_BRAVE_ENABLED` | `false` | Brave search |
| `PICOCLAW_TOOLS_WEB_BRAVE_API_KEY` | | Brave API key |
#### ✅ Advantages
- **Zero JSON knowledge needed** — just set key=value pairs
- **Easiest to set up** — add a few env vars and deploy
- **Good for quick testing** — get running in under a minute
#### ❌ Limitations
- **No custom providers** — only the built-in providers are supported (Gemini, OpenRouter, OpenAI, Anthropic, Groq, Mistral, DeepSeek, Zhipu, Moonshot, Nvidia, vLLM). You **cannot** add Ollama or other custom OpenAI-compatible providers
- **No custom API base URLs** — you can't override `api_base` for providers (needed for self-hosted models)
- **Limited channel support** — only Telegram, Discord, Slack, and LINE are configurable. Feishu, DingTalk, QQ, WhatsApp, MaixCam, and OneBot are **not** configurable via env vars
- **No proxy settings** — provider proxy configuration is not available
- **Hardcoded defaults** — many settings like `max_results`, `webhook_port`, etc. use hardcoded defaults that can't be changed
- **Allow-lists are comma-separated strings** — works but less flexible than JSON arrays (no spaces in IDs)
> **💡 Tip:** Start with Method 3 to get running quickly, then switch to Method 1 when you need more control.
---
## 🔧 Running Agent & Doctor Commands
The gateway service runs automatically. To run one-shot commands, SSH into your Coolify server and use:
```bash
# Run agent mode (one-shot question)
docker compose -f docker-compose-coolify.yml --profile agent run --rm picoclaw-agent -m "Hello!"
# Run agent mode (interactive)
docker compose -f docker-compose-coolify.yml --profile agent run --rm picoclaw-agent
# Run doctor (diagnostics)
docker compose -f docker-compose-coolify.yml --profile doctor run --rm picoclaw-doctor
```
### 🎮 Chat Commands (Telegram/Discord)
You can check status and swap models directly from your chat app:
| Command | Action |
|---------|--------|
| `/models` | View active model, provider, and all configured endpoints. |
| `/model <name>` | Switch the **model name** (keeping current provider). |
| `/model <provider>/<model>` | Switch **both** provider and model (e.g., `vllm/qwen3-coder-next:cloud`). |
> [!NOTE]
> Changes made via `/model` are active in memory. If the container restarts, it will revert to the default model defined in your `PICOCLAW_CONFIG_JSON` or environment variables.
---
## <20> Troubleshooting
### "No API key configured"
Your config isn't being loaded. Check:
- For Method 1: Is `PICOCLAW_CONFIG_JSON` set? Is it valid JSON?
- For Method 3: Is `PICOCLAW_PROVIDERS_GEMINI_API_KEY` set?
- Check container logs: `docker logs picoclaw-gateway` — look for the `📝 Using config from...` line.
### Container keeps restarting
Check logs: `docker logs picoclaw-gateway --tail 50`
Common issues:
- Invalid JSON in `PICOCLAW_CONFIG_JSON` (use a validator!)
- Missing API key for the configured provider
### Build fails
- Ensure you're using the `deploy/coolify` branch
- Check that both `Dockerfile.coolify` and `entrypoint-coolify.sh` exist in the repo
---
## 📁 File Structure (Coolify-specific)
```
picoclaw/
├── docker-compose-coolify.yml # Coolify-optimized compose file
├── Dockerfile.coolify # Coolify Dockerfile with entrypoint
├── entrypoint-coolify.sh # Config generator script
├── Dockerfile # Original Dockerfile (not used by Coolify)
├── docker-compose.yml # Original compose (not used by Coolify)
└── config/
└── config.example.json # Reference config with all options
```

41
Dockerfile.coolify Normal file
View file

@ -0,0 +1,41 @@
# ============================================================
# PicoClaw Dockerfile — Coolify Edition
# Uses an entrypoint script that generates config.json from
# environment variables at container startup.
# ============================================================
# Stage 1: Build the picoclaw binary
FROM golang:1.26.0-alpine AS builder
RUN apk add --no-cache git make
WORKDIR /src
# Cache dependencies
COPY go.mod go.sum ./
RUN go mod download
# Copy source and build
COPY . .
RUN make build
# Stage 2: Minimal runtime image
FROM alpine:3.23
RUN apk add --no-cache ca-certificates tzdata curl
# Copy binary
COPY --from=builder /src/build/picoclaw /usr/local/bin/picoclaw
# Create picoclaw home directory
RUN /usr/local/bin/picoclaw onboard
# Copy the entrypoint script that generates config from env vars
COPY entrypoint-coolify.sh /usr/local/bin/entrypoint-coolify.sh
RUN chmod +x /usr/local/bin/entrypoint-coolify.sh
# Default env vars (can be overridden by Coolify)
ENV TZ=UTC
ENTRYPOINT ["/usr/local/bin/entrypoint-coolify.sh"]
CMD ["gateway"]

View file

@ -45,11 +45,8 @@
> * **OFFICIAL DOMAIN:** The **ONLY** official website is **[picoclaw.io](https://picoclaw.io)**, and company website is **[sipeed.com](https://sipeed.com)** > * **OFFICIAL DOMAIN:** The **ONLY** official website is **[picoclaw.io](https://picoclaw.io)**, and company website is **[sipeed.com](https://sipeed.com)**
> * **Warning:** Many `.ai/.org/.com/.net/...` domains are registered by third parties. > * **Warning:** Many `.ai/.org/.com/.net/...` domains are registered by third parties.
> * **Warning:** picoclaw is in early development now and may have unresolved network security issues. Do not deploy to production environments before the v1.0 release. > * **Warning:** picoclaw is in early development now and may have unresolved network security issues. Do not deploy to production environments before the v1.0 release.
> * **Note:** picoclaw has recently merged a lot of PRs, which may result in a larger memory footprint (1020MB) in the latest versions. We plan to prioritize resource optimization as soon as the current feature set reaches a stable state.
## 📢 News ## 📢 News
2026-02-16 🎉 PicoClaw hit 12K stars in one week! Thank you all for your support! PicoClaw is growing faster than we ever imagined. Given the high volume of PRs, we urgently need community maintainers. Our volunteer roles and roadmap are officially posted [here](doc/picoclaw_community_roadmap_260216.md) —we cant wait to have you on board!
2026-02-13 🎉 PicoClaw hit 5000 stars in 4days! Thank you for the community! There are so many PRs&issues come in (during Chinese New Year holidays), we are finalizing the Project Roadmap and setting up the Developer Group to accelerate PicoClaw's development. 2026-02-13 🎉 PicoClaw hit 5000 stars in 4days! Thank you for the community! There are so many PRs&issues come in (during Chinese New Year holidays), we are finalizing the Project Roadmap and setting up the Developer Group to accelerate PicoClaw's development.
🚀 Call to Action: Please submit your feature requests in GitHub Discussions. We will review and prioritize them during our upcoming weekly meeting. 🚀 Call to Action: Please submit your feature requests in GitHub Discussions. We will review and prioritize them during our upcoming weekly meeting.
@ -175,6 +172,32 @@ docker compose --profile gateway build --no-cache
docker compose --profile gateway up -d docker compose --profile gateway up -d
``` ```
### ☁️ Deploy on Coolify (3 Steps)
Deploy PicoClaw on [Coolify](https://coolify.io) in under 5 minutes:
**1. Create service** — In Coolify: **+ New** → **Docker Compose** → connect your fork of this repo
- Branch: `main`
- Compose file: `docker-compose-coolify.yml`
**2. Add env vars** — Go to **Environment Variables** tab and add:
| Variable | Value |
|----------|-------|
| `PICOCLAW_PROVIDERS_GEMINI_API_KEY` | Your [Gemini API key](https://aistudio.google.com/apikey) |
| `PICOCLAW_CHANNELS_TELEGRAM_ENABLED` | `true` *(if using Telegram)* |
| `PICOCLAW_CHANNELS_TELEGRAM_TOKEN` | Your bot token from [@BotFather](https://t.me/BotFather) |
| `TZ` | `Asia/Kolkata` *(or your timezone)* |
**3. Deploy!** 🚀
> [!TIP]
> **Need more control?** (custom providers like Ollama, allowlists, multiple channels)
> Set `PICOCLAW_CONFIG_JSON` with your full config as a single env var.
> See the complete guide: **[COOLIFY.md](COOLIFY.md)**
---
### 🚀 Quick Start ### 🚀 Quick Start
> [!TIP] > [!TIP]

167
docker-compose-coolify.yml Normal file
View file

@ -0,0 +1,167 @@
# ============================================================
# PicoClaw — Coolify-Optimized Docker Compose
# ============================================================
#
# DEPLOYMENT STEPS (Coolify):
# 1. Create a new service → Docker Compose
# 2. Point source to this repository
# 3. Set "Docker Compose file" path to: picoclaw/docker-compose-coolify.yml
# 4. Add environment variables in Coolify's UI (see below)
# 5. Deploy!
#
# RECOMMENDED: Use PICOCLAW_CONFIG_JSON for full control:
# PICOCLAW_CONFIG_JSON — Full JSON config (overrides everything below)
#
# ALTERNATIVE: Individual environment variables:
# GEMINI_API_KEY — Your Gemini API key
#
# OPTIONAL ENVIRONMENT VARIABLES:
# LLM_PROVIDER — LLM provider (e.g. gemini, vllm, openrouter)
# LLM_MODEL — Model name (e.g. gemini-2.5-flash-lite)
# TELEGRAM_BOT_TOKEN — Telegram bot token
# DISCORD_BOT_TOKEN — Discord bot token
# OPENROUTER_API_KEY — OpenRouter API key
# OPENAI_API_KEY — OpenAI API key
# ANTHROPIC_API_KEY — Anthropic API key
# GROQ_API_KEY — Groq API key (also enables voice transcription)
# BRAVE_SEARCH_API_KEY — Brave Search API key
# LINE_CHANNEL_SECRET — LINE channel secret
# LINE_CHANNEL_ACCESS_TOKEN — LINE channel access token
# TZ — Timezone (default: UTC)
#
# ============================================================
services:
# ─────────────────────────────────────────────
# PicoClaw Gateway (Long-running Bot)
# This is the main service that Coolify will auto-start
# ─────────────────────────────────────────────
picoclaw-gateway:
build:
context: .
dockerfile: Dockerfile.coolify
container_name: picoclaw-gateway
restart: unless-stopped
# ── Environment Variables ────────────────────
# If PICOCLAW_CONFIG_JSON is set, it takes full priority.
# Individual vars below are only used as fallback (Method 3).
environment:
# -- Full JSON config (Method 1 — overrides everything below) --
- PICOCLAW_CONFIG_JSON=${PICOCLAW_CONFIG_JSON:-}
# -- Core LLM Config (only used if PICOCLAW_CONFIG_JSON is empty) --
- LLM_PROVIDER=${LLM_PROVIDER:-}
- LLM_MODEL=${LLM_MODEL:-}
# -- Provider API Keys --
- GEMINI_API_KEY=${GEMINI_API_KEY:-}
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY:-}
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
- GROQ_API_KEY=${GROQ_API_KEY:-}
# -- Chat Channel Tokens --
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN:-}
- DISCORD_BOT_TOKEN=${DISCORD_BOT_TOKEN:-}
- LINE_CHANNEL_SECRET=${LINE_CHANNEL_SECRET:-}
- LINE_CHANNEL_ACCESS_TOKEN=${LINE_CHANNEL_ACCESS_TOKEN:-}
# -- Web Search --
- BRAVE_SEARCH_API_KEY=${BRAVE_SEARCH_API_KEY:-}
- BRAVE_SEARCH_ENABLED=${BRAVE_SEARCH_ENABLED:-}
- DUCKDUCKGO_ENABLED=${DUCKDUCKGO_ENABLED:-}
# -- Heartbeat --
- HEARTBEAT_ENABLED=${HEARTBEAT_ENABLED:-}
- HEARTBEAT_INTERVAL=${HEARTBEAT_INTERVAL:-}
# -- Timezone --
- TZ=${TZ:-UTC}
# ── Volumes ──────────────────────────────────
volumes:
# Persistent workspace — sessions, memory, logs survive redeploys
# Note: config.json is created by 'picoclaw onboard' during Docker build
# and all settings are overridden via environment variables above
- picoclaw-workspace:/root/.picoclaw/workspace
# ── Ports ────────────────────────────────────
# Expose ports if using LINE webhook or MaixCAM channel
# Coolify will auto-detect and configure these
ports:
- "18790:18790" # MaixCAM / Gateway
- "18791:18791" # LINE Webhook
# ── Health Check ─────────────────────────────
# Coolify uses this to determine if the container is healthy
healthcheck:
test: ["CMD", "picoclaw", "status"]
interval: 60s
timeout: 10s
retries: 3
start_period: 30s
# ── Command ──────────────────────────────────
command: ["gateway"]
# ── Logging ──────────────────────────────────
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# ─────────────────────────────────────────────
# PicoClaw Agent (One-shot / Interactive)
# Not auto-started by Coolify (has profile).
# Run manually via SSH:
# docker compose -f docker-compose-coolify.yml run --rm picoclaw-agent -m "Hello"
# docker compose -f docker-compose-coolify.yml run --rm picoclaw-agent
# ─────────────────────────────────────────────
picoclaw-agent:
build:
context: .
dockerfile: Dockerfile
container_name: picoclaw-agent
profiles:
- agent
environment:
- PICOCLAW_AGENTS_DEFAULTS_PROVIDER=${LLM_PROVIDER:-}
- PICOCLAW_AGENTS_DEFAULTS_MODEL=${LLM_MODEL:-}
- PICOCLAW_PROVIDERS_GEMINI_API_KEY=${GEMINI_API_KEY:-}
- PICOCLAW_PROVIDERS_OPENROUTER_API_KEY=${OPENROUTER_API_KEY:-}
- PICOCLAW_PROVIDERS_OPENAI_API_KEY=${OPENAI_API_KEY:-}
- PICOCLAW_PROVIDERS_ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
- TZ=${TZ:-UTC}
volumes:
- picoclaw-workspace:/root/.picoclaw/workspace
entrypoint: ["picoclaw", "agent"]
stdin_open: true
tty: true
# ─────────────────────────────────────────────
# PicoClaw Doctor (Diagnostic Tool)
# Not auto-started by Coolify (has profile).
# Run manually via SSH:
# docker compose -f docker-compose-coolify.yml run --rm picoclaw-doctor
# ─────────────────────────────────────────────
picoclaw-doctor:
build:
context: .
dockerfile: Dockerfile
container_name: picoclaw-doctor
profiles:
- doctor
environment:
- PICOCLAW_AGENTS_DEFAULTS_PROVIDER=${LLM_PROVIDER:-}
- PICOCLAW_AGENTS_DEFAULTS_MODEL=${LLM_MODEL:-}
- PICOCLAW_PROVIDERS_GEMINI_API_KEY=${GEMINI_API_KEY:-}
- TZ=${TZ:-UTC}
volumes:
- picoclaw-workspace:/root/.picoclaw/workspace
entrypoint: ["picoclaw", "doctor"]
volumes:
picoclaw-workspace:
driver: local

170
entrypoint-coolify.sh Normal file
View file

@ -0,0 +1,170 @@
#!/bin/sh
# ============================================================
# PicoClaw Coolify Entrypoint
# Generates config.json before starting PicoClaw
#
# CONFIG PRIORITY (first match wins):
# 1. PICOCLAW_CONFIG_JSON env var — paste your entire JSON config
# 2. Mounted file at /config/config.json — use Coolify Storages
# 3. Auto-generated from individual env vars (basic setup)
# ============================================================
set -e
CONFIG_DIR="/root/.picoclaw"
CONFIG_FILE="${CONFIG_DIR}/config.json"
mkdir -p "${CONFIG_DIR}"
# ─────────────────────────────────────────────────
# METHOD 1: Full JSON config via env var
# Set PICOCLAW_CONFIG_JSON in Coolify env vars
# with your entire config.json content
# ─────────────────────────────────────────────────
if [ -n "${PICOCLAW_CONFIG_JSON}" ]; then
echo "📝 Using config from PICOCLAW_CONFIG_JSON env var"
echo "${PICOCLAW_CONFIG_JSON}" > "${CONFIG_FILE}"
# CRITICAL: Unset all PICOCLAW_* env vars (except PICOCLAW_CONFIG_JSON)
# so that Go's env.Parse() does NOT override values from the JSON file.
# Without this, Dockerfile ENV defaults (e.g. PICOCLAW_AGENTS_DEFAULTS_PROVIDER=gemini)
# would silently overwrite the user's JSON config every time.
for var in $(env | grep '^PICOCLAW_' | cut -d= -f1); do
if [ "$var" != "PICOCLAW_CONFIG_JSON" ]; then
unset "$var"
fi
done
exec picoclaw "$@"
fi
# ─────────────────────────────────────────────────
# METHOD 2: Mounted config file
# In Coolify → Storages → Add:
# Source: /data/coolify/applications/<uuid>/config.json
# Destination: /config/config.json
# Then paste your JSON in the file content
# ─────────────────────────────────────────────────
if [ -f "/config/config.json" ]; then
echo "📝 Using mounted config from /config/config.json"
cp /config/config.json "${CONFIG_FILE}"
# Same protection as Method 1: unset PICOCLAW_* env vars
for var in $(env | grep '^PICOCLAW_' | cut -d= -f1); do
unset "$var"
done
exec picoclaw "$@"
fi
# ─────────────────────────────────────────────────
# METHOD 3: Auto-generate from individual env vars
# Good for simple setups (Gemini + Telegram, etc.)
# ─────────────────────────────────────────────────
echo "📝 Generating config from individual env vars"
# Helper: comma-separated string → JSON array
csv_to_json_array() {
input="$1"
if [ -z "$input" ]; then echo "[]"; return; fi
result="["
first=true
OLD_IFS="$IFS"; IFS=","
for item in $input; do
item=$(echo "$item" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
if [ -n "$item" ]; then
if [ "$first" = true ]; then first=false; else result="${result},"; fi
result="${result}\"${item}\""
fi
done
IFS="$OLD_IFS"
echo "${result}]"
}
# ── Resolve env vars ─────────────────────────────
# Accept both Coolify-friendly short names and PICOCLAW_* internal names
R_PROVIDER="${PICOCLAW_AGENTS_DEFAULTS_PROVIDER:-${LLM_PROVIDER:-gemini}}"
R_MODEL="${PICOCLAW_AGENTS_DEFAULTS_MODEL:-${LLM_MODEL:-gemini-2.5-flash-lite}}"
R_GEMINI_KEY="${PICOCLAW_PROVIDERS_GEMINI_API_KEY:-${GEMINI_API_KEY:-}}"
R_OPENROUTER_KEY="${PICOCLAW_PROVIDERS_OPENROUTER_API_KEY:-${OPENROUTER_API_KEY:-}}"
R_OPENAI_KEY="${PICOCLAW_PROVIDERS_OPENAI_API_KEY:-${OPENAI_API_KEY:-}}"
R_ANTHROPIC_KEY="${PICOCLAW_PROVIDERS_ANTHROPIC_API_KEY:-${ANTHROPIC_API_KEY:-}}"
R_GROQ_KEY="${PICOCLAW_PROVIDERS_GROQ_API_KEY:-${GROQ_API_KEY:-}}"
R_MISTRAL_KEY="${PICOCLAW_PROVIDERS_MISTRAL_API_KEY:-${MISTRAL_API_KEY:-}}"
R_DEEPSEEK_KEY="${PICOCLAW_PROVIDERS_DEEPSEEK_API_KEY:-${DEEPSEEK_API_KEY:-}}"
R_VLLM_KEY="${PICOCLAW_PROVIDERS_VLLM_API_KEY:-${VLLM_API_KEY:-}}"
R_VLLM_BASE="${PICOCLAW_PROVIDERS_VLLM_API_BASE:-${VLLM_API_BASE:-}}"
R_TELEGRAM="${PICOCLAW_CHANNELS_TELEGRAM_TOKEN:-${TELEGRAM_BOT_TOKEN:-}}"
R_DISCORD="${PICOCLAW_CHANNELS_DISCORD_TOKEN:-${DISCORD_BOT_TOKEN:-}}"
R_LINE_SECRET="${PICOCLAW_CHANNELS_LINE_CHANNEL_SECRET:-${LINE_CHANNEL_SECRET:-}}"
R_LINE_ACCESS="${PICOCLAW_CHANNELS_LINE_CHANNEL_ACCESS_TOKEN:-${LINE_CHANNEL_ACCESS_TOKEN:-}}"
R_BRAVE_KEY="${PICOCLAW_TOOLS_WEB_BRAVE_API_KEY:-${BRAVE_SEARCH_API_KEY:-}}"
R_BRAVE_ON="${PICOCLAW_TOOLS_WEB_BRAVE_ENABLED:-${BRAVE_SEARCH_ENABLED:-false}}"
R_DDG_ON="${PICOCLAW_TOOLS_WEB_DUCKDUCKGO_ENABLED:-${DUCKDUCKGO_ENABLED:-true}}"
R_HB_ON="${PICOCLAW_HEARTBEAT_ENABLED:-${HEARTBEAT_ENABLED:-true}}"
R_HB_INT="${PICOCLAW_HEARTBEAT_INTERVAL:-${HEARTBEAT_INTERVAL:-30}}"
# ── Auto-enable channels when token is provided ──
if [ -n "$R_TELEGRAM" ]; then TG_ON="true"; else TG_ON="${PICOCLAW_CHANNELS_TELEGRAM_ENABLED:-false}"; fi
if [ -n "$R_DISCORD" ]; then DC_ON="true"; else DC_ON="${PICOCLAW_CHANNELS_DISCORD_ENABLED:-false}"; fi
if [ -n "$R_LINE_SECRET" ] && [ -n "$R_LINE_ACCESS" ]; then LN_ON="true"; else LN_ON="${PICOCLAW_CHANNELS_LINE_ENABLED:-false}"; fi
TELEGRAM_ALLOW=$(csv_to_json_array "${PICOCLAW_CHANNELS_TELEGRAM_ALLOW_FROM:-}")
DISCORD_ALLOW=$(csv_to_json_array "${PICOCLAW_CHANNELS_DISCORD_ALLOW_FROM:-}")
SLACK_ALLOW=$(csv_to_json_array "${PICOCLAW_CHANNELS_SLACK_ALLOW_FROM:-}")
LINE_ALLOW=$(csv_to_json_array "${PICOCLAW_CHANNELS_LINE_ALLOW_FROM:-}")
cat > "${CONFIG_FILE}" <<ENDOFCONFIG
{
"agents": {
"defaults": {
"workspace": "~/.picoclaw/workspace",
"restrict_to_workspace": true,
"provider": "${R_PROVIDER}",
"model": "${R_MODEL}",
"max_tokens": ${PICOCLAW_AGENTS_DEFAULTS_MAX_TOKENS:-8192},
"temperature": ${PICOCLAW_AGENTS_DEFAULTS_TEMPERATURE:-0.7},
"max_tool_iterations": ${PICOCLAW_AGENTS_DEFAULTS_MAX_TOOL_ITERATIONS:-20}
}
},
"channels": {
"telegram": { "enabled": ${TG_ON}, "token": "${R_TELEGRAM}", "allow_from": ${TELEGRAM_ALLOW} },
"discord": { "enabled": ${DC_ON}, "token": "${R_DISCORD}", "allow_from": ${DISCORD_ALLOW} },
"slack": { "enabled": ${PICOCLAW_CHANNELS_SLACK_ENABLED:-false}, "bot_token": "${PICOCLAW_CHANNELS_SLACK_BOT_TOKEN:-}", "app_token": "${PICOCLAW_CHANNELS_SLACK_APP_TOKEN:-}", "allow_from": ${SLACK_ALLOW} },
"line": { "enabled": ${LN_ON}, "channel_secret": "${R_LINE_SECRET}", "channel_access_token": "${R_LINE_ACCESS}", "webhook_host": "0.0.0.0", "webhook_port": 18791, "webhook_path": "/webhook/line", "allow_from": ${LINE_ALLOW} },
"maixcam": { "enabled": false, "host": "0.0.0.0", "port": 18790, "allow_from": [] },
"whatsapp": { "enabled": false, "bridge_url": "ws://localhost:3001", "allow_from": [] },
"feishu": { "enabled": false, "app_id": "", "app_secret": "", "encrypt_key": "", "verification_token": "", "allow_from": [] },
"dingtalk": { "enabled": false, "client_id": "", "client_secret": "", "allow_from": [] },
"onebot": { "enabled": false, "ws_url": "ws://127.0.0.1:3001", "access_token": "", "reconnect_interval": 5, "group_trigger_prefix": [], "allow_from": [] }
},
"providers": {
"gemini": { "api_key": "${R_GEMINI_KEY}", "api_base": "${PICOCLAW_PROVIDERS_GEMINI_API_BASE:-}" },
"openrouter": { "api_key": "${R_OPENROUTER_KEY}", "api_base": "${PICOCLAW_PROVIDERS_OPENROUTER_API_BASE:-}" },
"openai": { "api_key": "${R_OPENAI_KEY}", "api_base": "${PICOCLAW_PROVIDERS_OPENAI_API_BASE:-}" },
"anthropic": { "api_key": "${R_ANTHROPIC_KEY}", "api_base": "${PICOCLAW_PROVIDERS_ANTHROPIC_API_BASE:-}" },
"groq": { "api_key": "${R_GROQ_KEY}", "api_base": "${PICOCLAW_PROVIDERS_GROQ_API_BASE:-}" },
"mistral": { "api_key": "${R_MISTRAL_KEY}", "api_base": "${PICOCLAW_PROVIDERS_MISTRAL_API_BASE:-}" },
"zhipu": { "api_key": "${PICOCLAW_PROVIDERS_ZHIPU_API_KEY:-}", "api_base": "${PICOCLAW_PROVIDERS_ZHIPU_API_BASE:-}" },
"moonshot": { "api_key": "${PICOCLAW_PROVIDERS_MOONSHOT_API_KEY:-}", "api_base": "${PICOCLAW_PROVIDERS_MOONSHOT_API_BASE:-}" },
"deepseek": { "api_key": "${R_DEEPSEEK_KEY}", "api_base": "${PICOCLAW_PROVIDERS_DEEPSEEK_API_BASE:-}" },
"nvidia": { "api_key": "${PICOCLAW_PROVIDERS_NVIDIA_API_KEY:-}", "api_base": "${PICOCLAW_PROVIDERS_NVIDIA_API_BASE:-}" },
"vllm": { "api_key": "${R_VLLM_KEY}", "api_base": "${R_VLLM_BASE}" }
},
"tools": {
"web": {
"brave": { "enabled": ${R_BRAVE_ON}, "api_key": "${R_BRAVE_KEY}", "max_results": 5 },
"duckduckgo": { "enabled": ${R_DDG_ON}, "max_results": 5 }
},
"firecrawl": { "enabled": ${PICOCLAW_TOOLS_FIRECRAWL_ENABLED:-false}, "api_key": "${PICOCLAW_TOOLS_FIRECRAWL_API_KEY:-}", "api_base": "https://api.firecrawl.dev/v1" },
"serpapi": { "enabled": ${PICOCLAW_TOOLS_SERPAPI_ENABLED:-false}, "api_key": "${PICOCLAW_TOOLS_SERPAPI_API_KEY:-}", "max_results": 10 }
},
"heartbeat": { "enabled": ${R_HB_ON}, "interval": ${R_HB_INT} },
"devices": { "enabled": false, "monitor_usb": false },
"gateway": { "host": "0.0.0.0", "port": 18790 }
}
ENDOFCONFIG
exec picoclaw "$@"