
DocuSign のような SaaS を使わずに電子署名を社内で完結させたい、あるいは既存の業務システムに署名フローを API で組み込みたい——そういう要件を持ったエンジニアが調べ始めるのが DocuSeal です。
OSS なのでコードを読めるし、Docker で動くし、API もある。
Docker Compose の最小構成から Webhook 受信まで、一通り手を動かしながら整理します。
セルフホストを選ぶ3つの動機——コスト・データ主権・API 組み込み
DocuSign をはじめとする SaaS 型の電子署名サービスは、使い始めるのは簡単ですが、月額費用がシート数や送信件数に連動して膨らみやすい構造になっています。
件数が読めないプロジェクトや、コスト管理を厳しく求められる現場では、予算の見通しが立てにくいという声をよく聞きます。
2つ目の動機は データ主権 です。
契約書や秘密保持契約、個人情報を含む書類を外部クラウドに送出することに、法務やセキュリティ部門が難色を示すケースは少なくありません。
セルフホストであれば、ドキュメントは自社サーバーの外に出ません。
3つ目は 既存システムへの深い組み込み です。
「署名依頼を自動で飛ばしたい」「署名完了を受け取って社内 DB を更新したい」という処理を Rails や Laravel のコードベースに組み込もうとすると、SaaS の API では認証まわりや Webhook 設計に余計な複雑さが生まれがちです。
セルフホストの DocuSeal なら、同じネットワーク内に置いてしまえば API キーだけで叩けます。
DocuSeal 自体は Ruby on Rails 製の OSS で、ライセンスは AGPL-3.0 です。
ストレージはローカルディスク・S3 互換・Google Cloud Storage・Azure Blob Storage に対応しています。
フィールドタイプは Signature / Date / File / Checkbox など12種類が使えます。
電子署名の法的有効性については、国・文書種別・同意フローの設計によって扱いが変わるため、法務部門への確認を別途行ってください。
Docker Compose で最小構成を立ち上げる
デフォルトの DocuSeal は SQLite 単体で動きます。
手元で試すだけならそれで十分ですが、本番では PostgreSQL + Redis の構成を選ぶべきです。
Rails のバックグラウンドジョブ処理に Sidekiq を使っており、Redis がないとメール送信や PDF 生成のキューが動きません。
services:
docuseal:
image: docuseal/docuseal:latest
depends_on:
- postgres
- redis
ports:
- "3000:3000"
volumes:
- docuseal_data:/data
env_file:
- .env
environment:
DATABASE_URL: postgres://docuseal:${POSTGRES_PASSWORD}@postgres:5432/docuseal
REDIS_URL: redis://redis:6379/0
HOST: ${HOST}
FORCE_SSL: ${FORCE_SSL}
postgres:
image: postgres:16-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: docuseal
POSTGRES_USER: docuseal
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
volumes:
docuseal_data:
postgres_data:
redis_data:
ボリュームを3つ定義しているのがポイントです。
docuseal_data にはアップロードされた PDF や署名済みファイルが入るので、named volume にしておかないとコンテナ再起動のたびにデータが消えます。
次に .env ファイルです。
# アプリ設定
HOST=sign.example.com
FORCE_SSL=true
# DB
POSTGRES_PASSWORD=your_strong_password_here
# SMTP
SMTP_ADDRESS=smtp.sendgrid.net
SMTP_PORT=587
SMTP_USERNAME=apikey
SMTP_PASSWORD=your_sendgrid_api_key
SMTP_AUTHENTICATION=plain
SMTP_ENABLE_STARTTLS_AUTO=true
SMTP_FROM=noreply@example.com
SMTP_AUTHENTICATION を none にして認証なしの内部 SMTP リレーを使うことも可能です(社内メールサーバーを使う場合など)。
リバースプロキシで 3000 番ポートを HTTPS 終端する際は、 WebSocket サポートを必ず有効にしてください。
DocuSeal はリアルタイムの署名 UI に WebSocket を使っているため、これを忘れると署名画面が途中で固まります。
Nginx Proxy Manager を使っている場合は「Websockets Support」のチェックを入れるだけです。
あわせて proxy_read_timeout を 300 秒程度に伸ばしておくと、低速回線のユーザーが PDF レンダリング中にタイムアウトするトラブルを防げます。
FORCE_SSL はリバースプロキシ側で SSL 終端する構成の場合、プロキシとコンテナ間は HTTP のままにして false にするケースもあります。
構成に合わせて調整してください。
署名フローを作って送付する——管理画面と API の両面から
コンテナが起動したら、設定した HOST にブラウザでアクセスします。
初回アクセス時はセットアップウィザードが表示され、管理者アカウントのメールアドレスとパスワードを設定するだけで使い始められます。
特別な初期化コマンドは不要です。
ログイン後、「Templates」から PDF をアップロードすると、ページ単位でドラッグ&ドロップで署名フィールドを配置できます。
Signature(手書き署名)・Date・Checkbox・Text・File など12種類のフィールドが使えて、複数の署名者(Submitter)をロールとして定義することもできます。
「契約書の甲・乙それぞれに署名欄を置く」といった構成も、テンプレート作成時に設定できます。
送付方法は2パターンあります。
管理画面から手動で「Send」ボタンを押してメールアドレスを入力する方法と、API 経由で送付する方法です。
既存システムへの組み込みでは後者が主役になります。
curl -X POST https://sign.example.com/api/templates/1/submissions \
-H "X-Auth-Token: your_api_token" \
-H "Content-Type: application/json" \
-d '{
"send_email": true,
"submitters": [
{
"role": "First Party",
"email": "tanaka@example.com",
"name": "田中 太郎"
},
{
"role": "Second Party",
"email": "suzuki@example.com",
"name": "鈴木 花子"
}
]
}'
API トークンは管理画面の「API Tokens」から発行できます。
send_email を true にすると、設定した SMTP 経由で署名依頼メールが各 submitter に自動送信されます。
メール送信を自前で制御したい場合は send_email を false にして署名用 URL だけ取得し、自社のメール基盤から送ることもできます。
署名完了 Webhook を受け取る
DocuSeal が発火するイベントは6種類あります。
- submission.created — 署名依頼が作成されたとき
- submission.completed — 全員の署名が完了したとき
- submission.archived — 署名依頼がアーカイブされたとき
- form.started — 署名者がフォームを開始したとき
- form.viewed — 署名者がフォームを閲覧したとき
- form.completed — 個々の署名者がフォームを完了したとき
実務上は submission.completed を起点に処理を組むケースがほとんどです。
「全員の署名が揃ったら社内 DB のステータスを更新して担当者に通知する」という流れが典型的です。
Webhook URL の登録は管理画面の「Webhooks」から行います。
URL と検証用のシークレットトークンを設定するだけで、ペイロードは JSON で届きます。
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhooks/docuseal', async (req, res) => {
const event = req.body;
if (event.event_type !== 'submission.completed') {
return res.status(200).json({ received: true });
}
const submissionId = event.data?.id;
const documents = event.data?.documents ?? [];
const signedPdfUrls = documents.map(doc => ({
name: doc.name,
url: doc.url,
}));
// 社内 DB 更新・通知処理などをここで行う
// await updateContractStatus(submissionId, 'completed');
// await notifyStaff(signedPdfUrls);
res.status(200).json({ received: true });
});
app.listen(3001);
ひとつ見落としやすい点があります。
DocuSeal は Webhook の自動リトライを持っていません。
エンドポイントがダウンしていたりタイムアウトしたりした場合、そのイベントは再送されません。
本番運用では、受信したイベントをまず DB やキューに積んで「受け取った」とだけ返し、実際の処理は非同期で行う設計にするのが安全です。
同じ submission_id を2回受け取っても副作用が出ない冪等処理も合わせて入れておきましょう。
本番に持ち込む前に最低限やること
ここまでで「動く」状態にはなりましたが、本番投入の前に確認しておきたい点を整理します。
- HTTPS 終端と WebSocket サポート — リバースプロキシで WebSocket を通さないと署名 UI が壊れます。proxy_read_timeout も 300 秒以上に設定しておきましょう。
- ストレージの永続化 — ローカルボリュームで運用する場合、サーバー障害時のリスクがあります。件数が増えてきたら S3 互換ストレージ(MinIO や AWS S3)への切り替えを検討してください。環境変数を渡すだけで切り替えられます。
- メール配信の信頼性 — 自前の SMTP サーバーを使う場合、迷惑メールに分類されて署名依頼が届かないトラブルが起きやすいです。SendGrid・Amazon SES・Postmark などのトランザクションメールサービスを使うほうが到達率の面で安定します。
- DB バックアップ — pg_dump をサイドカーコンテナや cron で回して、ダンプファイルを外部ストレージに退避するパターンが手軽です。
- Webhook リトライ設計 — 自動リトライがないことを前提に、エンドポイント側で受信漏れを検知する仕組みを設けてください。
- アップデート戦略 — イメージのバージョンを latest に固定せず、変更履歴を確認しながら計画的に上げることをおすすめします。AGPL-3.0 のため、改変して社外に提供する場合はソース開示義務が生じる点も確認しておきましょう。
リソースの目安として、Hetzner CX22(2 vCPU・4GB RAM・40GB ディスク)相当のサーバーで、月数百件規模の署名フローを PostgreSQL・アプリ・日次バックアップコンテナと同居させて問題なく動いているという実績があります。
小〜中規模の社内利用であれば、最初から大きなインスタンスを用意する必要はありません。
DocuSeal は「とりあえず動かして試す」までのハードルが低い一方、本番で安定させるための考慮点はそれなりにあります。
ただ、その考慮点のほとんどは DocuSeal 固有の話ではなく、セルフホストのウェブアプリ全般に共通する話です。
すでに Docker 運用の経験があるチームなら、特別な学習コストなく本番に持ち込めると思います。
株式会社ホコサキは山口県宇部市を拠点に、Web 制作・業務システム開発・AI 活用支援・DX 推進に取り組んでいます。
DocuSeal のような OSS を既存システムに組み込む支援や、セルフホスト環境の設計・構築もお手伝いできます。
ご相談は お問い合わせページ からどうぞ。

