Skip to content

Manual Setup

Use this guide if you want full control: no setup-server.sh, no Docker. You install every component yourself. Recommended only if the scripted paths don't fit your environment.

1. Install dependencies

# Ubuntu 22.04 / 24.04
sudo apt update
sudo apt install -y curl git build-essential ca-certificates tmux ufw fail2ban

# Node.js 22 (via NodeSource)
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs

# pnpm
corepack enable
corepack prepare pnpm@9.15.4 --activate

# PostgreSQL 16 + pgvector
sudo apt install -y postgresql-16 postgresql-16-pgvector

2. Configure PostgreSQL

sudo -u postgres psql <<SQL
CREATE USER doable WITH PASSWORD 'change-me';
CREATE DATABASE doable OWNER doable;
\c doable
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE EXTENSION IF NOT EXISTS pgvector;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
SQL

Edit /etc/postgresql/16/main/postgresql.conf:

listen_addresses = 'localhost'

Restart PostgreSQL:

sudo systemctl restart postgresql

3. Clone & build Doable

sudo mkdir -p /opt/doable
sudo chown $USER /opt/doable
git clone https://github.com/doable-me/doable.git /opt/doable
cd /opt/doable
pnpm install
pnpm build

4. Create the env file

cp .env.example .env

Edit .env and set at least:

JWT_SECRET=$(openssl rand -hex 32)
ENCRYPTION_KEY=$(openssl rand -hex 32)
INTERNAL_SECRET=$(openssl rand -hex 32)
DATABASE_URL=postgres://doable:change-me@localhost:5432/doable

API_HOST=127.0.0.1
WS_HOST=127.0.0.1
NODE_ENV=production

NEXT_PUBLIC_API_URL=https://api.your-domain.com
NEXT_PUBLIC_WS_URL=wss://ws.your-domain.com
NEXT_PUBLIC_APP_URL=https://your-domain.com
CORS_ORIGINS=https://your-domain.com

Add an AI provider key:

ANTHROPIC_API_KEY=sk-ant-...
# or OPENAI_API_KEY, or COPILOT_CLI_PATH

See the full Environment Variables reference.

5. Run migrations

pnpm db:migrate

6. Reverse proxy

Pick one. Both work. Both must terminate TLS.

Caddy (easiest — auto-SSL)

/etc/caddy/Caddyfile:

your-domain.com {
    bind 127.0.0.1
    reverse_proxy 127.0.0.1:3000
}

api.your-domain.com {
    bind 127.0.0.1
    reverse_proxy 127.0.0.1:4000
}

ws.your-domain.com {
    bind 127.0.0.1
    reverse_proxy 127.0.0.1:4001
}

The bind 127.0.0.1 directive matters when you're routing public traffic via Cloudflare Tunnel. Drop it (or use bind 0.0.0.0) if Caddy is your public-facing entrypoint.

nginx

Use docker/nginx.conf.template as a starting point. The Docker setup script substitutes {{DOMAIN}} and writes the result to /etc/nginx/sites-enabled/doable.

7. systemd unit

Create /etc/systemd/system/doable.service:

[Unit]
Description=Doable (tmux session: api, web, ws)
After=network.target postgresql.service

[Service]
Type=forking
User=doable
WorkingDirectory=/opt/doable
ExecStart=/usr/bin/tmux new-session -d -s doable \
  'pnpm dev:api'  \; \
  new-window -n web 'pnpm dev:web' \; \
  new-window -n ws  'pnpm dev:ws'
ExecStop=/usr/bin/tmux kill-session -t doable
Restart=on-failure

[Install]
WantedBy=multi-user.target

For production, replace pnpm dev:* with pnpm start:* after a pnpm build. The dev scripts are convenient but trade memory for hot-reload.

Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable --now doable
sudo systemctl status doable

8. Firewall

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp comment 'SSH'
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Verify nothing else is listening publicly:

ss -tlnp | grep -v 127.0.0.1 | grep -v '\[::1\]'
# Only port 22 (sshd) should remain.

9. Optional pieces

Feature What to install
Chromium for thumbnails sudo apt install -y chromium-browser fonts-liberation libasound2t64 libnss3
Cloudflare Tunnel Follow Cloudflare's install guide, then cloudflared tunnel route dns <id> your-domain.com
Redis sudo apt install -y redis-server, then REDIS_URL=redis://127.0.0.1:6379 in .env

10. Verify

curl -fsSL https://your-domain.com/         | head -1
curl -fsSL https://api.your-domain.com/health

Open https://your-domain.com in a browser and sign up.

→ For OAuth, integrations, and other extras, see Environment Variables and User Guide / Integrations.