UbuntuでWindmillをセルフホストする:Docker構築とPostgreSQLのトラブルシューティング完全ガイド
ワークフロー自動化プラットフォームは現代の開発チームに不可欠ですが、Windmill Cloudなどのクラウドサービスは、利用が増えるにつれて費用がかさむことがあります。Docker ComposeとTraefikを使い、構築を妨げるPostgreSQLの認証問題を解決しながら、Ubuntu上で自社のWindmillインスタンスを構築する方法を紹介します。
構築するもの
このチュートリアルを終えると、以下を構築できます:
- HTTPSに対応した、正常に動作するWindmill環境
- Traefik経由のLet’s Encryptによる自動SSL証明書
- 適切な認証を備えた本番運用向けPostgreSQLデータベース
- リソース使用量を最適化したワーカー設定
- 既存のDockerインフラとの統合
- 業務のワークフロー自動化に使える本番運用向けの構成
月額コスト: 税別(プライマリIPv4アドレス1個込み)5.99ユーロから(Hetzner CX23、4GB RAM、2026年10月時点)+ ドメイン費用。同じインフラで複数の自動化ツールを運用できます
前提条件
- DockerとDocker ComposeがインストールされたUbuntu 24.04 LTSサーバー
- 既存のTraefikリバースプロキシセットアップ(Traefik設定についてはHetzner Cloudでn8nをセルフホストする:Docker構築完全ガイドを参照してください)
- サーバーのIPアドレスを指すドメイン名
- 最低4GB RAMと2 vCPUを推奨
- SSHアクセスと基本的なコマンドラインの知識
Windmillの概要
Windmillは以下を提供するオープンソースのワークフローエンジンです:
- TypeScript/Python/Goサポート付きのビジュアルワークフローエディター
- ジョブスケジューリングと実行管理
- API連携機能
- チームでの共同作業機能
- セルフホスティング対応。無料のCommunity Editionでは実行回数が無制限
n8nのノードベースのアプローチとは異なり、Windmillは強力な開発環境を備えたコードを中心に記述するワークフローを重視しています。
ステップ1:サーバーの準備とディレクトリ構造
まず、サーバー環境を準備します。競合を避けるために、Windmillインスタンスには食べ物の名前を使った命名規則を使用します:
# Create Windmill directory (first instance: "pizza")
mkdir -p /opt/windmill-pizza
cd /opt/windmill-pizza
# Create required subdirectories
mkdir -p postgres-data windmill-data lsp-cache
# Verify directory structure
ls -la
命名規則: 複数のWindmillインスタンスにはシンプルな食べ物の名前を使用します:
- 最初のインスタンス:
pizza - 追加インスタンス:
pasta、salad、soup、burgerなど - 競合を回避し、管理を容易にします
ステップ2:環境設定
適切な認証情報を含む安全な環境ファイルを作成します:
# Generate a secure hex password (no special characters!)
SECURE_PASSWORD=$(openssl rand -hex 16)
echo "Generated password: $SECURE_PASSWORD"
# Create environment file
cat > /opt/windmill-pizza/.env << EOF
# Windmill Image Version
WM_IMAGE=ghcr.io/windmill-labs/windmill:main
# Database Configuration - SECURE PASSWORDS
DATABASE_URL=postgresql://postgres:${SECURE_PASSWORD}@windmill-pizza-db:5432/windmill_pizza?sslmode=disable
POSTGRES_PASSWORD=${SECURE_PASSWORD}
POSTGRES_DB=windmill_pizza
POSTGRES_USER=postgres
# Windmill Configuration
BASE_URL=https://windmill.yourdomain.com
RUST_LOG=info
# Worker Configuration
WORKER_GROUP=default
KEEP_JOB_DIR=false
# Instance Identifier
INSTANCE_NAME=pizza
EOF
chmod 600 /opt/windmill-pizza/.env
重要: windmill.yourdomain.comを実際のドメインに置き換えてください。
ステップ3:Docker Compose設定
メインのDocker Compose設定を作成します:
cat > /opt/windmill-pizza/docker-compose.yml << 'EOF'
version: "3.8"
services:
# PostgreSQL Database for Windmill
windmill-db:
image: postgres:16
container_name: windmill-pizza-db
restart: unless-stopped
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
volumes:
- ./postgres-data:/var/lib/postgresql/data
networks:
- proxy
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
# Windmill Server
windmill-server:
image: ${WM_IMAGE}
container_name: windmill-pizza-server
restart: unless-stopped
environment:
- DATABASE_URL=${DATABASE_URL}
- BASE_URL=${BASE_URL}
- RUST_LOG=${RUST_LOG}
- MODE=server
networks:
- proxy
depends_on:
windmill-db:
condition: service_healthy
volumes:
- ./windmill-data:/tmp/windmill
labels:
- "traefik.enable=true"
- "traefik.http.routers.windmill-pizza.rule=Host(`windmill.yourdomain.com`)"
- "traefik.http.routers.windmill-pizza.entrypoints=https"
- "traefik.http.routers.windmill-pizza.tls.certresolver=letsencrypt"
- "traefik.http.services.windmill-pizza.loadbalancer.server.port=8000"
# Windmill Worker
windmill-worker:
image: ${WM_IMAGE}
container_name: windmill-pizza-worker
restart: unless-stopped
environment:
- DATABASE_URL=${DATABASE_URL}
- BASE_URL=${BASE_URL}
- RUST_LOG=${RUST_LOG}
- MODE=worker
- WORKER_GROUP=${WORKER_GROUP}
- KEEP_JOB_DIR=${KEEP_JOB_DIR}
networks:
- proxy
depends_on:
windmill-db:
condition: service_healthy
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./windmill-data:/tmp/windmill
- worker_dependency_cache:/tmp/windmill/cache
deploy:
resources:
limits:
cpus: '1'
memory: 2G
networks:
proxy:
external: true
volumes:
worker_dependency_cache:
driver: local
EOF
重要: Traefikラベルのドメインを実際の構成に合わせて更新してください。
ステップ4:PostgreSQLパスワード問題(重大な問題)
多くのWindmillの構築で失敗が起きるのが、この部分です。根本原因を特定するまでに、かなりの調査が必要でした:
問題:パスワード内の特殊文字
openssl rand -base64 32を使用してパスワードを生成すると、出力に+、/、=が含まれることがあります。DATABASE_URL内では、これらの文字をパーセントエンコーディングする必要があります。エンコードしないとURLの解析が正しく行われず、PostgreSQLの認証失敗として現れることがあります。16進数のパスワードを使えば、このエンコード処理自体が不要になります。
問題のあるパスワードの例:
# This WILL cause authentication failures:
PASSWORD="305t6m9KrC/kvbyNEF+EYQ6pqAGlApn9rbJPH3D5y9g="
解決策:16進数のみのパスワード
特殊文字を含まない16進数のみのパスワードを使用します:
# This WORKS reliably:
PASSWORD=$(openssl rand -hex 16)
# Example result: 5781b14ec0ec1bc184653ffa5e379411
追加のPostgreSQL設定の問題
- ユーザー設定:Windmillの設定例と同様に、スーパーユーザーの
postgresを使用します。カスタムユーザーを使うには、Windmillのマイグレーションが必要とするwindmill_adminとwindmill_userのロールを付与する必要があります(Windmillのセルフホスティングのドキュメントを参照) - 永続ボリューム:既存のデータボリュームに異なる認証情報が含まれている場合、PostgreSQLは
POSTGRES_PASSWORD環境変数を無視 - URL形式:Windmillの設定例のように、データベースに内部Dockerネットワークからのみアクセスできる場合は、データベースURLに
?sslmode=disableを含めます。ほかのネットワーク経由でアクセスするデータベースには、requireなどのSSLモードを使用します
ステップ5:インストールと起動
修正した設定でWindmillをインストールします:
cd /opt/windmill-pizza
# Verify configuration syntax
docker compose config --quiet
# Pull images
docker compose pull
# Start services
docker compose up -d
# Check status
docker compose ps
以下のような出力が表示されるはずです:
NAME STATUS PORTS
windmill-pizza-db Up (healthy) 5432/tcp
windmill-pizza-server Up 8000/tcp
windmill-pizza-worker Up 8000/tcp
ステップ6:よくある問題のトラブルシューティング
問題1:PostgreSQL認証の失敗
症状:
windmill-pizza-server | Error: password authentication failed for user "postgres"
解決策:この操作はWindmillのデータベース内の全データを削除します。新規インストール時、または検証済みのバックアップがある場合にのみ実行してください。2つのsedコマンドが、新しいパスワードをPOSTGRES_PASSWORDとDATABASE_URLに書き込みます。
# Stop containers
docker compose down --volumes
# Remove old data
rm -rf postgres-data/*
# Regenerate hex password
NEW_PASSWORD=$(openssl rand -hex 16)
sed -i "s/POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=$NEW_PASSWORD/" .env
sed -i "s#postgresql://postgres:[^@]*@#postgresql://postgres:$NEW_PASSWORD@#" .env
# Restart
docker compose up -d
問題2:コンテナが起動しない
症状:
- コンテナが即座に終了
- リソース割り当てエラー
解決策:
# Check container logs
docker logs windmill-pizza-server
docker logs windmill-pizza-db
# Check system resources
docker stats
free -h
問題3:SSL証明書の問題
症状:
- HTTPSが機能しない
- 証明書エラー
解決策:
# Check Traefik logs
docker logs traefik
# Verify DNS resolution
nslookup windmill.yourdomain.com
# Restart Traefik if needed
docker restart traefik
ステップ7:アクセスと初期セットアップ
インストールが完了したら:
- Windmillにアクセス:https://windmill.yourdomain.com
- デフォルト認証情報:
- メール:
[email protected] - パスワード:
changeme
- メール:
- セットアップを完了:
- 管理者パスワードを変更
- ベースURLを設定
- ユーザーアカウントをセットアップ
ステップ8:リソース最適化
メモリ割り当て(8GBサーバーの場合)
当社の設定では、リソースを効率的に割り当てます:
- Windmillサーバー:約800MB
- Windmillワーカー:2GB(制限付き)
- PostgreSQL:約500MB
- システム用に確保する容量:約4.7GB
CPU割り当て(4 vCPUサーバーの場合)
- ワーカー:1 vCPU(制限付き)
- その他のサービス:3 vCPU(共有)
スケーリングルール: vCPUあたり1ワーカー、各1〜2GB RAM
ステップ9:本番環境のセキュリティ強化
バックアップスクリプトの作成
cat > /opt/windmill-pizza/backup.sh << 'EOF'
#!/bin/bash
set -euo pipefail
cd /opt/windmill-pizza
DATE=$(date +%Y%m%d_%H%M%S)
FILE=/opt/backups/windmill_pizza_${DATE}.sql
mkdir -p /opt/backups
if ! docker compose exec -T windmill-db pg_dump -U postgres windmill_pizza > "$FILE"; then
rm -f "$FILE"
echo "Backup failed" >&2
exit 1
fi
echo "Backup created: $FILE"
EOF
chmod +x /opt/windmill-pizza/backup.sh
監視のセットアップ
# Monitor container health
docker compose ps
# Check resource usage
docker stats
# Monitor logs
docker compose logs -f windmill-server
自動アップデートの設定
cat > /opt/windmill-pizza/update.sh << 'EOF'
#!/bin/bash
cd /opt/windmill-pizza
docker compose pull
docker compose up -d --force-recreate
EOF
chmod +x /opt/windmill-pizza/update.sh
ステップ10:高度な設定
既存SMTPとの連携
メールサーバーがある場合(当社のHetzner Cloudでn8nをセルフホストする:Docker構築完全ガイドのように)、連携させます:
# Add to docker-compose.yml environment for windmill-server:
- SMTP_HOST=mailserver
- SMTP_PORT=25
- SMTP_USERNAME=
- SMTP_PASSWORD=
- [email protected]
複数のWindmillインスタンス
分離された環境を必要とするチーム向け:2つ目のインスタンスには、空のデータディレクトリ、専用のデータベースとパスワードを用意します。
# Create second instance with its own empty data directories
mkdir -p /opt/windmill-pasta/postgres-data /opt/windmill-pasta/windmill-data /opt/windmill-pasta/lsp-cache
cp /opt/windmill-pizza/.env /opt/windmill-pizza/docker-compose.yml /opt/windmill-pasta/
chmod 600 /opt/windmill-pasta/.env
# Update configuration
sed -i 's/pizza/pasta/g' /opt/windmill-pasta/.env
sed -i 's/windmill.yourdomain.com/pasta.yourdomain.com/g' /opt/windmill-pasta/.env
# Generate new password
NEW_PASSWORD=$(openssl rand -hex 16)
sed -i "s/POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=$NEW_PASSWORD/" /opt/windmill-pasta/.env
sed -i "s#postgresql://postgres:[^@]*@#postgresql://postgres:$NEW_PASSWORD@#" /opt/windmill-pasta/.env
# Update docker-compose.yml
sed -i 's/pizza/pasta/g' /opt/windmill-pasta/docker-compose.yml
sed -i 's/windmill.yourdomain.com/pasta.yourdomain.com/g' /opt/windmill-pasta/docker-compose.yml
セキュリティの考慮事項
ネットワーク分離
- PostgreSQLには共有Dockerネットワーク
proxy上のコンテナからのみアクセス可能。より厳密に分離するには、別の内部ネットワークを使用 - データベースのポートを外部に公開しない
- TraefikレベルでのHTTPS終端
リソース制限
- ワーカーコンテナにCPUとメモリの制限あり
- リソース制限により、暴走するジョブやリソース枯渇を狙う試みの影響を軽減
- サーバー容量に基づいて設定可能
SSLセキュリティ
- Let’s Encrypt証明書の自動取得
- HTTPからHTTPSへのリダイレクト
- 最新のTLS設定
監視とメンテナンス
週次ヘルスチェック
# Container status
docker compose ps | grep -E "(healthy|Up)"
# Resource usage
docker stats --no-stream
# Log analysis
docker compose logs | grep -i error
月次メンテナンス
# Backup database first
cd /opt/windmill-pizza
./backup.sh
# Update containers
./update.sh
# Clean old data
docker system prune -f
コスト内訳と比較
月額コスト
セルフホスティングセットアップ:
- Hetzner CX23(4GB RAM):税別(プライマリIPv4アドレス1個込み)月額5.99ユーロ(2026年10月時点)
- ドメイン費用:約月額1ユーロ
- 合計:税別月額約7ユーロ
Windmill Cloudとの比較:
- クラウド:開発者席あたり月額20ドル、オペレーターあたり月額10ドル(2026年10月時点)
- 節約額:2人以上の開発者が席を必要とする場合、年間250ドル以上
パフォーマンスのメリット
セルフホスティングの利点:
- 無制限のワークフロー実行
- 外部レート制限なし
- 完全なデータ制御
- 独自のシステム連携
- リソースを柔軟に拡張可能
トラブルシューティングの早見表
簡易診断
# Container health
docker compose ps | grep -E "(healthy|Up)"
# Network connectivity
docker network inspect proxy
# Database connection
docker compose exec windmill-db psql -U postgres -d windmill_pizza -c "SELECT version();"
# Log analysis
docker compose logs --tail 50 windmill-server | grep -E "(ERROR|WARN)"
よくあるエラーパターン
- 「password authentication failed」 → 16進数パスワードを使用。ボリュームをクリアするのは、バックアップ後または新規インストール時のみ
- 「connection refused」 → ネットワーク設定を確認
- 「certificate errors」 → DNSとTraefikセットアップを検証
- 「out of memory」 → ワーカーのリソース制限を調整
Windmillインフラのスケーリング
水平スケーリング
大量処理環境向け:
# Add additional workers
windmill-worker-2:
image: ${WM_IMAGE}
container_name: windmill-pizza-worker-2
environment:
- DATABASE_URL=${DATABASE_URL}
- MODE=worker
- WORKER_GROUP=heavy
deploy:
resources:
limits:
cpus: '2'
memory: 4G
垂直スケーリング
サーバーリソースをアップグレード(Hetznerの税別(プライマリIPv4アドレス1個込み)価格、2026年10月時点):
- CX33(8GB RAM):重いワークロード向けに月額8.99ユーロ
- CX43(16GB RAM):企業での利用向けに月額16.49ユーロ
既存インフラとの統合
n8nとの連携
すでにn8nを運用している場合(当社のHetzner Cloudでn8nをセルフホストする:Docker構築完全ガイドに沿った構成など):
- Windmillはコードを中心に記述するワークフローを処理
- n8nは画面上で組み立てるシンプルな自動化を処理
- 両方が同じTraefikプロキシを共有
- 別々のデータベースで競合を防止
共有サービス
既存のインフラを活用:
- Traefik:すべてのサービスのSSLを処理
- メールサーバー:通知用の共有SMTP
- 監視:統一されたログとメトリクス
- バックアップ:一元化されたバックアップ戦略
まとめ
Windmillのセルフホスティングは、クラウドホスティングコストのごく一部で企業向けのワークフロー自動化を提供します。成功の鍵は、PostgreSQLの認証情報がどのように受け渡され、永続化されるかを理解し、インストールを妨げる可能性のあるURLエンコーディングの問題を回避するために16進数のみのパスワードを使用することです。
このセットアップの主な利点
- コスト効率:クラウドソリューションと比較して年間数百ドルを節約
- 本番対応:企業のワークロードを確実に処理
- 安全:HTTPS、データベースポートの非公開化、リソース制限(Dockerソケットのマウントにより、ワーカーには引き続きホストレベルのアクセス権があります)
- 拡張性:必要に応じてワーカーとリソースを簡単に追加
- プライバシー:ワークフローが外部サービスに送信しない限り、コードとデータは自社のインフラ内に保持されます
この設定は本番環境でテスト済みであり、事業に不可欠なワークフロー自動化に必要な信頼性を提供します。トラブルシューティングの手順は、デプロイ中に発生した実際の問題、特に多くのセルフホスト型環境に影響するPostgreSQL認証の問題に対処しています。
複雑なワークフローや企業向けのデプロイでは、用途に合った最適化と適切なリソース配分について、専門家への相談をご検討ください。
次のステップ
- Windmillにも適用できるWebhookのトラブルシューティング手法を確認
- 大規模データセットの処理に向けたペイロード最適化を確認
- 既存のHetzner Cloudでn8nをセルフホストする:Docker構築完全ガイドとの連携を検討
tvaについて
tvaは、データベースシステム、クラウド環境、グローバルサプライチェーンの包括的なインフラ管理を担っています。当社の体系的なアプローチは、厳格なセキュリティプロトコルとパフォーマンス最適化を組み合わせ、戦略的アドバイザリーサービスにより、デジタル機能と物理的資産の両方を的確に連携させ、すべての支援案件で最高水準の運用品質とコンプライアンスを維持しています。
当社のサービスおよび追加の自動化チュートリアルの詳細については、 tva.sg をご覧ください。