Craftplan

Self-Hosting

Quick Start

Deploy Craftplan without cloning the repo. Just download the Compose file and start:

# 1. Download the Compose file and env template
curl -O https://raw.githubusercontent.com/puemos/craftplan/main/docker-compose.yml
curl -O https://raw.githubusercontent.com/puemos/craftplan/main/.env.example

# 2. Create your environment file and fill in secrets (see below)
cp .env.example .env

# 3. Start everything
docker compose up -d

Craftplan will be available at http://localhost:4000 (or the PORT you configured). Migrations run automatically on startup.


Single Container (docker run)

If you already have a PostgreSQL database and S3-compatible storage, you can run Craftplan as a single container:

docker run -d \
  --name craftplan \
  -p 4000:4000 \
  -e SECRET_KEY_BASE="your-secret-key-base" \
  -e TOKEN_SIGNING_SECRET="your-token-signing-secret" \
  -e CLOAK_KEY="your-cloak-key" \
  -e DATABASE_URL="ecto://postgres:password@your-db-host/craftplan" \
  -e HOST="localhost" \
  -e PORT="4000" \
  -e AWS_ACCESS_KEY_ID="your-access-key" \
  -e AWS_SECRET_ACCESS_KEY="your-secret-key" \
  -e AWS_S3_BUCKET="craftplan" \
  -e AWS_S3_SCHEME="https://" \
  -e AWS_S3_HOST="s3.amazonaws.com" \
  -e AWS_REGION="us-east-1" \
  ghcr.io/puemos/craftplan:latest

Or with an env file:

docker run -d \
  --name craftplan \
  --env-file .env \
  -e DATABASE_URL="ecto://postgres:password@your-db-host/craftplan" \
  -p 4000:4000 \
  ghcr.io/puemos/craftplan:latest

Deploy on Railway

Deploy on Railway

Railway provisions a PostgreSQL database automatically. After deploying:

  1. Set the required environment variables (SECRET_KEY_BASE, TOKEN_SIGNING_SECRET, CLOAK_KEY) in the Railway dashboard.
  2. Set HOST to your Railway-provided domain.
  3. For file storage, configure an external S3-compatible provider (e.g., AWS S3, Cloudflare R2, Tigris) using the AWS_* environment variables.

Note: Railway does not include S3-compatible storage, so file uploads require an external provider.


Generating Secrets

Your .env file requires several secrets. Generate them with openssl:

SECRET_KEY_BASE and TOKEN_SIGNING_SECRET

Both are random 64-byte strings. Generate each one separately:

openssl rand -base64 48

Run the command twice — once for SECRET_KEY_BASE and once for TOKEN_SIGNING_SECRET.

CLOAK_KEY

A 32-byte AES key, base64-encoded:

openssl rand -base64 32

POSTGRES_PASSWORD

A password for the bundled PostgreSQL container (not needed if you supply your own DATABASE_URL):

openssl rand -base64 16

Configuration Reference

All configuration is done via environment variables in your .env file.

Required

VariableDescription
SECRET_KEY_BASEPhoenix secret for signing cookies and sessions
TOKEN_SIGNING_SECRETSecret for API token signing
CLOAK_KEY32-byte base64-encoded AES encryption key
POSTGRES_PASSWORDPassword for the PostgreSQL database

Host & Networking

VariableDefaultDescription
HOSTlocalhostPublic hostname (used in generated URLs)
PORT4000HTTP port the server listens on

File Storage (S3 / MinIO)

The bundled MinIO container works out of the box. Override these to use an external S3-compatible provider:

VariableDefaultDescription
MINIO_ROOT_USERminioadminMinIO access key
MINIO_ROOT_PASSWORDminioadminMinIO secret key
AWS_S3_BUCKETcraftplanS3 bucket name
AWS_S3_SCHEMEhttps://S3 endpoint scheme (http:// for MinIO)
AWS_ACCESS_KEY_ID-External S3 access key
AWS_SECRET_ACCESS_KEY-External S3 secret key
AWS_S3_HOSTs3.amazonaws.comS3 endpoint hostname
AWS_REGIONus-east-1S3 region
AWS_ASSET_HOST-Public URL prefix for uploaded assets

Email (Optional)

Email is primarily configured from the Settings UI inside the app. Environment variables are a fallback for automated or headless setups.

VariableDescription
EMAIL_PROVIDEROne of: sendgrid, postmark, brevo, mailgun, amazon_ses
EMAIL_API_KEYAPI key for the chosen provider
EMAIL_API_DOMAINMailgun domain (Mailgun only)
EMAIL_API_SECRETSecret key (Amazon SES only)
EMAIL_API_REGIONAWS region (Amazon SES only, default: us-east-1)
SMTP_HOSTSMTP relay hostname (alternative to API providers)
SMTP_PORTSMTP port (default: 587)
SMTP_USERNAMESMTP username
SMTP_PASSWORDSMTP password

File Storage

Bundled MinIO

The default docker-compose.yml includes a MinIO container that provides S3-compatible object storage. It creates a craftplan bucket automatically and requires no additional configuration.

The MinIO console is available at http://localhost:9001 with the credentials from your .env.

External S3

To use AWS S3 or another S3-compatible provider, set the AWS_* variables in your .env and remove or comment out the minio service from docker-compose.yml.


Reverse Proxy

For production deployments, place Craftplan behind a reverse proxy that handles TLS. Here’s an example nginx configuration:

upstream craftplan {
    server 127.0.0.1:4000;
}

server {
    listen 443 ssl http2;
    server_name your-domain.com;

    ssl_certificate     /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    location / {
        proxy_pass http://craftplan;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Make sure to set HOST to your public domain (e.g. your-domain.com).

Note: The Upgrade and Connection headers are required for Phoenix LiveView’s WebSocket connection.


Updating

docker compose pull
docker compose up -d

Migrations run automatically on each startup via the bin/server entrypoint.


Building from Source

If you are contributing to Craftplan or need a custom build, use the build-from-source Compose file:

git clone https://github.com/puemos/craftplan.git
cd craftplan
cp .env.example .env   # fill in secrets
docker compose -f docker-compose.prod.yml up -d

Or build the image manually:

docker build -t craftplan .

See docker-compose.prod.yml for the full build-from-source configuration.