iPhone showing Happy app controlling Claude Code on a tropical beach

Self-Host Happy Server for Claude Code on iPhone/iPad (Free)

Want to control Claude Code from your iPhone or iPad? The Happy mobile app makes this possible - you can run prompts, review code, and manage sessions right from your phone.

But there’s a catch: Happy’s servers aren’t always reliable. And sending your code through someone else’s infrastructure? Not ideal.

The fix: self-host it. It’s free, private, and takes about an hour.

TL;DR: Deploy happy-server-light via Docker, use Tailscale for private HTTPS access, configure the Happy iOS app to use your server. Full reliability, zero cost, complete privacy.

Note: When I say “we” throughout this post, I mean me and Claude Code. Claude helped research options, write the Docker configs, debug issues, and document the whole process.

Why Self-Host?

Three reasons:

  1. Reliability - My server, my uptime
  2. Privacy - Traffic stays on my network
  3. Learning - Fun side project

The Setup

After some research, I landed on this stack:

  • happy-server-light - A lightweight fork that uses SQLite instead of PostgreSQL. No Redis needed. Perfect for personal use.
  • Tailscale - Zero-config VPN mesh. My devices connect securely without exposing anything to the public internet.
  • Hetzner - Already had a server running there (or use whatever service you like for a server.

What You’ll Need

  • A Linux server (I use Hetzner, but any VPS works)
  • Docker and Docker Compose installed
  • Tailscale account (free tier is fine)
  • Happy app on your phone

Step 1: Install Tailscale Everywhere

On your server:

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

Follow the auth link. Note your server’s Tailscale IP (looks like 100.x.x.x).

Install Tailscale on your Mac and iPhone too. Same account. Now all your devices can talk to each other privately.

Step 2: Clone and Configure

cd /opt
git clone https://github.com/leeroybrun/happy-server-light.git
cd happy-server-light

The repo has TypeScript build issues. I worked around this by creating a custom Dockerfile that runs TypeScript directly instead of compiling:

# Dockerfile.fixed
FROM node:20
RUN apt-get update && apt-get install -y python3 ffmpeg && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY package.json yarn.lock ./
COPY ./prisma ./prisma
RUN yarn install --frozen-lockfile --ignore-engines
COPY ./tsconfig.json ./tsconfig.json
COPY ./sources ./sources
ENV NODE_ENV=production
EXPOSE 3005
CMD ["yarn", "start"]

Create docker-compose.yml:

services:
  happy-server-light:
    build:
      context: .
      dockerfile: Dockerfile.fixed
    container_name: happy-server-light
    ports:
      - "3005:3005"
    environment:
      - PORT=3005
      - DATABASE_URL=file:/data/happy-server-light.sqlite
      - PUBLIC_URL=https://your-server.your-tailnet.ts.net
      - HAPPY_SERVER_LIGHT_DATA_DIR=/data
      - HAPPY_SERVER_LIGHT_FILES_DIR=/data/files
    volumes:
      - happy-data:/data
    restart: unless-stopped

volumes:
  happy-data:

Build and start:

docker compose up -d

Initialize the database:

docker exec happy-server-light yarn db:push

Step 3: Enable HTTPS with Tailscale Serve

iOS blocks plain HTTP connections. Tailscale Serve creates an HTTPS proxy automatically:

sudo tailscale serve --bg 3005

First time, you’ll need to enable Serve in your Tailscale admin console when prompted.

Your server is now available at https://your-server.your-tailnet.ts.net

Step 4: Configure Your Devices

On Mac:

If you’re already logged into Happy’s official servers, clear the old credentials first:

rm ~/.happy/access.key

Add to ~/.zshrc:

export HAPPY_SERVER_URL=https://your-server.your-tailnet.ts.net

Then:

source ~/.zshrc
happy login

This generates a QR code and a login URL.

On iPhone/iPad:

  1. Make sure Tailscale VPN is connected
  2. Open Happy app
  3. If you’re already logged into Happy (using their servers), log out first
  4. Tap the Database icon in the top right corner
  5. Enter your custom server URL and verify it connects
  6. Copy the login URL from your Mac terminal and paste it in Safari on your phone (QR scanning was flaky for us)

Done!

Issues We Hit (and Fixed)

IssueFix
TypeScript build errorsCustom Dockerfile using yarn start (tsx) instead of building
401 errors on first requestRun docker exec happy-server-light yarn db:push to create tables
iOS “Failed to connect”iOS blocks HTTP. Use Tailscale Serve for automatic HTTPS
Can’t set custom server URLLog out of existing Happy session first, then Database icon appears
QR code scan not workingCopy the login URL from terminal and paste in Safari instead

Maintenance

# Restart
docker compose restart

# View logs
docker logs happy-server-light --tail 100 -f

# Update
git pull && docker compose build && docker compose up -d

# Backup
docker cp happy-server-light:/data/happy-server-light.sqlite ./backup.sqlite

Was It Worth It?

Yes. Took about an hour to set up (with some trial and error). Now I have reliable mobile access to Claude Code that doesn’t depend on someone else’s servers.

The combo of happy-server-light + Tailscale is simple and secure. No public exposure, no complex networking, no Redis or Postgres to manage.


FAQ

Is Happy open source?

Yes. Both the Happy mobile app and happy-server-light are open source. Your code is encrypted end-to-end using the same encryption as Signal.

Can I use this without Tailscale?

Yes, but you’d need to configure HTTPS yourself (Let’s Encrypt, Caddy, etc.) and expose your server to the public internet. Tailscale keeps everything private with zero config.

Does this work with Codex too?

Yes. Happy supports both Claude Code and OpenAI Codex. The same self-hosted server works for both.

What if the Happy iOS app gets updated?

Your self-hosted server should continue working. If something breaks, check the happy-server-light repo for updates.

How much does the server cost to run?

Minimal. A $5/month VPS handles it easily. I run it on a Hetzner server I already had.



Questions? Find me or contact me using the links on the left or in my sites menu.