Skip to content

Quick Start (Windows)

Run Doable on native Windows with PowerShell. No WSL, no Git Bash, no Linux VM. Just Docker Desktop plus the bundled setup.ps1 script.

This is the Windows sibling of the Docker quickstart. Same flags, same Caddy-in-docker TLS, same mkcert auto-trust. PowerShell 5.1 (built into Windows 10 and 11) is enough.

Prerequisites

  • Windows 10 or Windows 11.
  • Docker Desktop for Windows 4.0 or newer (ships Compose v2 built-in).
  • Git for Windows (only used to clone the repo).
  • About 25 GB free disk for a source build, or 5 GB if you use the prebuilt images via -Prebuilt.

Install in 60 seconds

git clone https://github.com/doable-me/doable.git
cd doable
.\deployment\docker\setup.ps1

That runs Doable on https://localhost with a self-signed cert that the script installs into the Windows root trust store using mkcert. Open https://localhost/signup once it finishes and the first account becomes the platform owner.

Three deployment modes

setup.ps1 mirrors setup.sh. Pick the mode that matches where the browser will live.

1. Localhost (default)

.\deployment\docker\setup.ps1
  • Listens on 127.0.0.1 only.
  • Downloads mkcert.exe to %LOCALAPPDATA%\doable\bin, installs a local CA into the current user's trust store, and issues a leaf cert for localhost, 127.0.0.1, and ::1.
  • Chrome, Edge, and Firefox all trust the cert after one restart.

2. Private network IP

.\deployment\docker\setup.ps1 -DoableHost 192.168.1.50 -InstallTrust
  • -DoableHost is the IP or LAN hostname clients on the same network will use. The parameter is named DoableHost because $Host is a PowerShell automatic variable.
  • -InstallTrust runs mkcert -install so the Windows machine itself trusts the cert. Skip the flag when the server is headless and the browser lives on a different box.
  • The script issues a leaf cert that covers the LAN IP plus localhost.

3. Public domain with Let's Encrypt

.\deployment\docker\setup.ps1 -Domain app.example.com -Email you@example.com
  • Caddy binds 0.0.0.0 and fetches a Let's Encrypt cert via ACME.
  • The domain must resolve to this machine's public IP and have ports 80 and 443 reachable from the internet.
  • -Email is optional but recommended so Let's Encrypt can email renewal warnings.

Behind Cloudflare Tunnel or another reverse proxy

.\deployment\docker\setup.ps1 -Domain app.example.com -SkipSsl
  • -SkipSsl (or $env:DOABLE_BEHIND_PROXY = '1') tells Caddy to bind 127.0.0.1 and use internal self-signed TLS, because the tunnel handles the public certificate.

Useful flags

Flag Effect
-Prebuilt Pull ghcr.io/doable-me/doable-* images instead of building from source. About 30 seconds vs 5 to 10 minutes.
-InstallTrust Force-install the mkcert CA into the Windows trust store. Always on in localhost mode, opt-in for -DoableHost.
-Email <addr> Contact email for Let's Encrypt registration in -Domain mode.
-SkipSsl Bind Caddy to loopback and issue an internal cert. Use this when a tunnel or CDN owns public TLS.
$env:DOABLE_AUTO_LOCALHOST = '1' Skip the interactive prompt and pick localhost mode (useful for unattended installs).
$env:DOABLE_KEEP_ENV = '1' Preserve the existing deployment/docker/.env instead of regenerating secrets.
$env:DOABLE_SKIP_DISK_CHECK = '1' Override the disk-space precheck.

Run Get-Help .\deployment\docker\setup.ps1 -Full for the complete parameter reference.

What the script does

  1. Verifies Docker Desktop is installed and the Compose plugin works.
  2. Runs a disk-space precheck (25 GB source build, 5 GB prebuilt).
  3. Generates deployment/docker/.env with cryptographically random JWT_SECRET, ENCRYPTION_KEY, INTERNAL_SECRET, DOABLE_KEK, POSTGRES_PASSWORD, and INSTALL_BOOTSTRAP_TOKEN (24 hour expiry).
  4. Tightens the ACL on .env to owner-only.
  5. Downloads mkcert and installs a local CA when running in localhost or -InstallTrust mode.
  6. Issues a leaf cert into deployment/docker/certs/.
  7. Pulls or builds the four service images (api, ws, web, migrate).
  8. Runs docker compose up -d.
  9. Waits up to 60 seconds for the one-shot migrate container to exit cleanly.
  10. Prints the URLs to open and the OAuth callback templates.

The same env vars set in your shell before launch are forwarded into the wizard's AI provider step, so $env:OPENAI_API_KEY = "sk-..." pre-configures the first run.

After the install

Open https://<your-host>/signup. The first signup is auto-promoted to platform owner. You then run through the five-step wizard at /setup (welcome, sign-in, AI provider, Cloudflare, plans and billing).

Common follow-up commands:

# Tail every container's logs
docker compose -f deployment/docker/docker-compose.yml logs -f

# Stop the stack
docker compose -f deployment/docker/docker-compose.yml down

# Restart after editing .env
docker compose -f deployment/docker/docker-compose.yml restart

# Edit configuration
notepad deployment/docker/.env

When you used -Prebuilt, swap docker-compose.yml for docker-compose.prod.yml in those commands.

Troubleshooting

Migration container exited with code 1. The most common cause is a stale postgres_data volume from an earlier install with a different password. Recover with:

docker compose -f deployment/docker/docker-compose.yml --env-file deployment/docker/.env down -v
.\deployment\docker\setup.ps1

Chrome still warns about the self-signed cert. Restart Chrome once after setup.ps1 finishes. Chrome 105 and newer reads the OS root store automatically. If your IT policy has set ChromeRootStoreEnabled=0, the cert lives in the right place but Chrome ignores it.

docker compose is not recognised. Update Docker Desktop. Compose v2 ships built-in from version 4.0 onward.

mkcert -install exits non-zero. The script logs to %TEMP%\doable-mkcert-install.log. Open it for the underlying error. You can re-run only the cert step by deleting deployment/docker/certs/ and running setup.ps1 again.

Disk precheck blocks the install. Either free space (docker system prune -a), or re-run with -Prebuilt (5 GB) or $env:DOABLE_SKIP_DISK_CHECK = '1'.

Next steps