← members

Run him on your own model

Point his mind at a model you run yourself. Your hardware does the thinking, and nothing is metered here.

Thanks to olie-o from our Discord for the original guide. This page follows its steps.

This is one of two ways to bring your own brain, and they are different things:

How it fits together

Your phone reaches your computer over Tailscale, a private network between your own devices. Tailscale Serve gives the computer an https address that only your devices can open, and hands each request to the model server running on it.

phone, running GrowBot
  │  https, inside your tailnet
  ▼
https://your-pc.your-tailnet.ts.net
  │  tailscale serve
  ▼
http://127.0.0.1:PORT on your computer
  └─ llama.cpp, Ollama or LM Studio

The phone needs an https address because growbot.dev is https, and the browser refuses to call plain http from an https page. Serve gives you one with a real certificate, keeps the same address tomorrow, and carries the stream his spoken turns use.

What you need

Step 1 · Start your server

Get the server answering on the computer first. Every later step depends on this one.

llama.cpp

llama-server -m your-model.gguf --port 8080 --reasoning off

This line sets port 8080. Recent llama.cpp builds use 9931 when you leave --port out (older ones used 8080), so keep it in, or use the port llama-server prints when it starts. --reasoning off is step 2, done ahead of time.

Ollama

Port 11434. The Ollama app starts the server for you. ollama list shows the model names you will type into GrowBot later. Ollama also needs step 3.

LM Studio

Port 1234. Load a model, then start the server from the Developer tab with Enable CORS switched on, or from a terminal:

lms server start --cors

Check it on the computer

Open this in the computer's own browser, with your port:

http://127.0.0.1:PORT/v1/models

You should see a short list of your models. If you do not, fix that before going on: nothing later can work around it.

Step 2 · Turn thinking off

Thinking models (qwen3, deepseek-r1 and others) write a hidden draft before they answer. GrowBot needs the answer straight away. The connection test allows only a few words, a thinking model spends all of them on the draft, the test fails, and he stays on our brain. olie-o found this one.

The thinking off box in GrowBot (ticked for a new setup, so tick it yourself if you saved a server before it existed) asks your server to skip the draft. It sends two fields, reasoning_effort and chat_template_kwargs. Current llama.cpp, Ollama and vLLM accept both, going by their source code. vLLM 0.10.1 to 0.17 and Ollama 0.11.5 to 0.12.3 refuse the first one. The test then asks once more with chat_template_kwargs alone, and saves the form that worked. We have not checked every version on real hardware, so if the test still says the model spent the whole answer thinking, turn it off on the server too:

Step 3 · Ollama only: let it answer the tailscale name

Skip this for llama.cpp and LM Studio.

Out of the box, Ollama listens only on 127.0.0.1 and refuses any request addressed to a name it does not recognise. Tailscale Serve passes your tailscale name straight through, so Ollama answers every request with a blank 403. In the phone's browser that shows up as HTTP ERROR 403. OLLAMA_ORIGINS does not fix that one. OLLAMA_HOST does.

Two settings, then restart Ollama:

Windows

Quit Ollama from the tray, run these in a terminal, then start Ollama again from the Start menu. setx only reaches programs started after it.

setx OLLAMA_HOST 0.0.0.0:11434
setx OLLAMA_ORIGINS https://growbot.dev

Or set the same two in Settings, under "Edit environment variables for your account".

Mac (the menu bar app)

Ollama's docs say to set each variable with launchctl setenv and then restart the Ollama application:

launchctl setenv OLLAMA_HOST "0.0.0.0:11434"
launchctl setenv OLLAMA_ORIGINS "https://growbot.dev"
# then quit and reopen Ollama

We are not going to tell you that survives a reboot, because Ollama's documentation does not say so. If the door stops working after you restart the Mac, set it again. The Expose Ollama to the network setting is stored by the app itself.

Linux (systemd)

sudo systemctl edit ollama.service
# add these two lines under [Service]:
Environment="OLLAMA_HOST=0.0.0.0:11434"
Environment="OLLAMA_ORIGINS=https://growbot.dev"
# save, then:
sudo systemctl daemon-reload
sudo systemctl restart ollama

Started by hand

That form only applies if you start the server yourself. Quit the Ollama app first.

OLLAMA_HOST=0.0.0.0:11434 OLLAMA_ORIGINS=https://growbot.dev ollama serve

What listening on 0.0.0.0 costs

Ollama has no password. Listening on 0.0.0.0 means anything on your home wifi can use it at http://YOUR-PC-IP:11434. On a home network you trust, that is usually fine. On shared wifi, at a café or in a dorm, it is not.

A tighter option is to listen on the computer's tailscale address only (OLLAMA_HOST=100.x.y.z:11434, the address tailscale ip -4 prints) and serve that address in step 4 with the line below. We have not tested that route, and Tailscale's Mac apps (the App Store and standalone builds) refuse a target that is not localhost.

tailscale serve --bg http://100.x.y.z:11434

Step 4 · Tailscale Serve

  1. Install Tailscale on the computer and on the phone, and sign in to the same account on both. On the computer, tailscale status should list both devices.
  2. On the computer, point Serve at your server's port. Run the one line for your server:
    tailscale serve --bg 8080    # llama.cpp
    tailscale serve --bg 11434   # Ollama
    tailscale serve --bg 1234    # LM Studio
    A bare port means http://127.0.0.1 on that port. --bg keeps it running after you close the terminal, and Tailscale brings it back by itself after a restart. The first time, Tailscale may print a link to turn on HTTPS for your tailnet: open it, approve, and run the line again.
  3. Check it:
    tailscale serve status
    It should show your https address marked (tailnet only), with a proxy line to 127.0.0.1 and your port. That address, https://YOUR-PC.YOUR-TAILNET.ts.net, is what the phone and GrowBot use.

To undo it: tailscale serve reset. On Linux, run these with sudo, or run sudo tailscale set --operator=$USER once. With the Mac App Store build, the command line tool is /Applications/Tailscale.app/Contents/MacOS/Tailscale. Older guides, including an earlier version of this page, show a serve https / form. Current Tailscale refuses it and prints the new form instead.

Use Serve, not Funnel. Serve is private to your tailnet. Funnel puts the address on the public internet, and a model server has no password of its own, so anyone who finds the address can use your graphics card.

Step 5 · Test from the phone

Turn Tailscale on in the phone's app, so it says Connected. Then open this in the phone's browser:

https://YOUR-PC.YOUR-TAILNET.ts.net/v1/models

You should see the same short list of models as in step 1. That is the Tailscale part done.

Step 6 · Set it in GrowBot

In the app: Brain → Settings → your own key → open the key panel. Scroll past the OpenRouter key to 🖥 DIY: YOUR OWN SERVER.

  1. Address: https://YOUR-PC.YOUR-TAILNET.ts.net/v1. It must end in /v1. GrowBot adds /chat/completions and nothing else, so without /v1 Ollama and LM Studio answer HTTP 404. llama.cpp works either way.
  2. Model name: the name your server lists at /v1/models. For Ollama that is the name ollama list shows, like qwen3:8b.
  3. Server api key: leave it empty unless your server or your proxy asks for one. It is sent as Authorization: Bearer.
  4. my model can see: tick it only if your model accepts images. See the warning below, this one matters.
  5. strict json mode: leave it on unless your server rejects it.
  6. thinking off: leave it on unless your server rejects it. The test tells you if it does.
  7. ⚡ TEST AND USE. It sends one throwaway line to your server, through the same streaming path his thoughts use, and only saves if a real answer comes back. Nothing of his goes with it: no memories, no soul, no conversation. ✕ CLEAR puts him back on our brain.
  8. If the test fails, nothing is written, so a server that was already working keeps working.

Do not give GrowBot http://127.0.0.1:8080 or http://localhost:1234. On the phone, those addresses mean the phone itself.

⚠ The one that catches people

"my model can see" is OFF until you tick it. Leave it off and he keeps talking normally while every camera frame is thrown away before it reaches your model. He does not go quiet. He guesses, and he sounds confident doing it.

He will still report light and dark correctly, because brightness reaches him as a number rather than a picture. So "he can tell the lamp is brighter but never sees the room" is the signature of this exact setting.

Tick it only if your model genuinely accepts images. If it does not, he is honestly better off telling you he cannot see.

When it does not work

The test quotes what went wrong. Find the words here.

What you seeWhat it means, and the fix
HTTP ERROR 403 in the phone's browser, or the test says your server answered HTTP 403 with an empty message Ollama is refusing the tailscale name. Do step 3: OLLAMA_HOST (or Expose Ollama to the network), then restart Ollama. OLLAMA_ORIGINS alone does not fix this.
The models list opens on the phone, but the test says "the browser would not tell us why, so this one is unresolved" Most often your server is not allowing this page (CORS). When a browser blocks a cross origin request it hides the reason from the page, so a blocked origin, a closed tunnel and a wrong address look the same from here. Ollama: OLLAMA_ORIGINS=https://growbot.dev, then restart it. LM Studio: switch on Enable CORS. llama.cpp allows any page by default. Started with --tools, --agent or MCP servers, it allows only localhost pages, because those flags put file and shell tools on its API. Start a plain llama-server for GrowBot instead of opening that one up.
"the model spent the whole answer thinking and never replied" (older versions of the app said "your server accepted the request and then sent nothing back") A thinking model. Do step 2.
HTTP 400 that mentions reasoning, chat_template_kwargs or think value, and "untick thinking off and test again" Your server does not accept the thinking off request. Untick thinking off, test again, and turn thinking off on the server instead (step 2). Ollama's version says "invalid think value". That Ollama is older than 0.12.4, and updating it fixes this.
HTTP 404 The address is missing /v1 at the end, or Serve points at the wrong port. Check tailscale serve status.
HTTP ERROR 502 in the phone's browser Serve is running, but nothing answers on the port it points at. Start your server (step 1) and check that tailscale serve status shows the same port. In the test this one shows up as unresolved.
HTTP 401 Your server or your proxy wants an api key. Put it in the server api key box.
The phone says the site can't be reached, or the test runs out of time The phone is not on your tailnet, the computer is asleep, or Serve is not running. Check that Tailscale says Connected on both devices, and run tailscale serve status on the computer.
"streaming: failed" and then "buffered: worked" Your server and your model answer, and something between them and the phone is not carrying the stream. A Cloudflare quick tunnel is one known cause, and a buffering proxy is another (see the proxy section).
It worked once and never came back A quick tunnel hands out a new random address every time it starts, so the saved one goes stale the moment you restart it. Use Tailscale Serve (step 4).
He talks but never sees The my model can see box, or a model without eyes.
Everything is slow Local rigs think slower than our proxy, and he waits longer before giving up. That is expected. The test itself waits twenty seconds, and a smaller model answers sooner.

Advanced: your own https reverse proxy

If you already run a web server with a real certificate, it can stand in for Tailscale. Tahuna from our Discord runs Ollama this way, through Apache on an internet facing box that forwards to the computer with the graphics card (his config is below). GrowBot only needs an https address that reaches /v1/chat/completions and passes the stream through unbuffered.

This one is on the open internet

An nginx sketch for Ollama on the same machine. We have not run this exact config, so treat it as a starting point:

location /v1/ {
    set $gb_ok 0;
    if ($request_method = OPTIONS) { set $gb_ok 1; }
    if ($http_authorization = "Bearer YOUR-LONG-RANDOM-TOKEN") { set $gb_ok 1; }
    if ($gb_ok = 0) { return 401; }
    proxy_pass http://127.0.0.1:11434;
    proxy_set_header Host localhost:11434;
    proxy_buffering off;
    proxy_read_timeout 300s;
}

Apache, built on Tahuna's config: Ollama on another computer on the home network, the https box in front. This version does what the card says. It forwards /v1/ only, refuses any request without the token, lets OPTIONS through, and still streams. We ran it on Apache 2.4.66 in front of a stand-in server, not a real Ollama. Put your Ollama computer's address in place of 192.168.1.24, and your token in place of YOUR-LONG-RANDOM-TOKEN. It needs mod_proxy, mod_proxy_http and mod_ssl.

<VirtualHost *:443>
    ServerName YOUR-HOST
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/YOUR-HOST/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/YOUR-HOST/privkey.pem
    <Location "/ollama/v1/">
        ProxyPass "http://192.168.1.24:11434/v1/"
        ProxyPassReverse "http://192.168.1.24:11434/v1/"
        <If "%{REQUEST_METHOD} != 'OPTIONS' && %{HTTP:Authorization} != 'Bearer YOUR-LONG-RANDOM-TOKEN'">
            Require all denied
        </If>
    </Location>
</VirtualHost>

GrowBot's server address is then https://YOUR-HOST/ollama/v1, and the token goes in the server api key box. A request without the right token gets 403 from Apache.

A 401 or 403 from the proxy carries no CORS header, so a missing or wrong token shows up in the test as unresolved. To check the token itself, run this on any computer (for the nginx sketch, leave out /ollama):

curl -H "Authorization: Bearer YOUR-LONG-RANDOM-TOKEN" https://YOUR-HOST/ollama/v1/models

Tahuna's original, which he runs today, is below for reference. Do not copy it as it is: it forwards all of Ollama's API, including pull and delete, to the internet with no token. It works because his Ollama is on another computer, so it listens beyond loopback and its name check is off (the first bullet at the top of this section). With ProxyPreserveHost Off, Apache sends the ProxyPass address as the Host, so the RequestHeader set Host line changes nothing here. That line only takes effect with ProxyPreserveHost On.

# Tahuna's original: open to anyone, do not copy
<VirtualHost *:443>
    ServerName YOUR-HOST
    SSLCertificateFile /etc/letsencrypt/live/YOUR-HOST/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/YOUR-HOST/privkey.pem
    <Location "/ollama/">
        ProxyPass "http://192.168.1.24:11434/"
        ProxyPassReverse "http://192.168.1.24:11434/"
        ProxyPreserveHost Off
        RequestHeader set Host "localhost:11434"
    </Location>
</VirtualHost>

Other roads: Cloudflare tunnels

⚠ A Cloudflare quick tunnel cannot work here

His spoken turns stream. Cloudflare's own documentation says, of the free trycloudflare.com quick tunnels: "Quick Tunnels do not support Server-Sent Events (SSE)." A quick tunnel will happily pass an ordinary request, so a simple connection check can look perfect while every real turn dies.

The same page notes a quick tunnel gets a random subdomain each time you launch it. That is "it worked once and never came back": the first tunnel carried an ordinary check, it closed, and the new one had a different address.

So skip it. A quick tunnel pointed at Ollama would also put all of Ollama's API on a public address with no token.

A Cloudflare named tunnel, if you already own a domain. It keeps one hostname and it carries the stream. Cloudflare offers two forms: remotely managed, created in the dashboard and installed with a token, or locally managed from your own machine with a config file. The one below is locally managed. Either way, Cloudflare requires a domain added to your Cloudflare account before a tunnel can publish anything.

cloudflared tunnel login
cloudflared tunnel create growbot
cloudflared tunnel route dns growbot ollama.yourdomain.com
cloudflared tunnel run growbot

A named tunnel is on the open internet

Anyone who finds the hostname reaches whatever the tunnel points at, and a tunnel cannot check GrowBot's token by itself. So never point it straight at your model server. Point it at a proxy on the same computer that checks the token (the proxy section), and pass /v1/ only.

The routes go in ~/.cloudflared/config.yml:

ingress:
  - hostname: ollama.yourdomain.com
    path: ^/v1/
    service: http://localhost:8081
  - service: http_status:404

Port 8081 stands for your token checking proxy, for example the nginx sketch above listening on 127.0.0.1:8081. The proxy sends Ollama the Host it wants, so the tunnel needs no host header. Ollama still needs OLLAMA_ORIGINS from step 3. If that is more than you want to run, use Tailscale Serve.

ngrok also gives a free address that stays put, but its free plan meters requests and puts a warning page in front of browser traffic, and this page cannot add the header that skips it. If you already use ngrok, point it at a proxy that checks a token (like the ones above), never straight at your model server. If you do not, Tailscale Serve is less trouble.

Picking a model

Small models are fine for talking and poor at seeing. If you want him to see, you need a vision model, and a 7B class one will be slower than what he is used to. Vision costs more than people expect on local hardware.

If your model has no eyes, that is a real choice and not a failure. Tick nothing, and he will say plainly that he cannot see rather than inventing a room.

If you would rather stay in a terminal

The public agent harness runs him from the command line with no browser in the way, so no tunnel and no CORS:

BASE_URL=http://localhost:11434/v1 MODEL=qwen3:8b node reference-loop.mjs

It boots a fresh creature from seed. It cannot yet load a soul file grown in the app.

Stuck? Discord or info@growbot.dev. Tell us the server, the model, whether the vision box is ticked, and the exact words the test showed.

Sources: Ollama FAQ (OLLAMA_HOST, allowed web origins, setting environment variables, the nginx and Cloudflare examples), Tailscale Serve and the tailscale serve command, llama.cpp server README (--reasoning, --reasoning-budget, --cors-origins), LM Studio server settings, Cloudflare, Quick Tunnels (no SSE, testing only, random subdomain), Cloudflare, create a locally-managed tunnel (the domain requirement), Cloudflare, origin parameters (httpHostHeader), nginx proxy module and Apache mod_proxy.