Self-hosted e-signature: a practical guide (with a working example)

What it actually takes to run your own e-signature server — ESIGN/UETA evidence, PAdES-sealed audit trails, TLS/reverse-proxy/SMTP, and backups — with a copy-pasteable Docker setup. Vendor-neutral, with Lifted Sign as the worked example.

Last updated July 23, 2026 · facts verified against primary sources

Vendor install docs tell you how to run their tool. This is the vendor-neutral version: the whole landscape of self-hostable options, a working docker setup you can copy, and the operational gotchas — TLS, SMTP, backups, and a court-admissible audit trail — that decide whether your deployment holds up.

Why self-host an e-signature server

  • Data residency & control. Contracts, signer PII, and the audit trail live on infrastructure you control — not a vendor’s multi-tenant cloud. Air-gapped and region-locked deploys become the default, not an enterprise upsell.
  • Cost at volume. Per-envelope pricing punishes success. A self-hosted server’s marginal cost per signature is basically your own compute.
  • No lock-in. With an open-source, self-hosted tool you keep the code, the database, and the signed PDFs. Migration is a file copy, not a data-export negotiation.
  • Auditable trust. You can read exactly how consent, hashing, and sealing work — instead of trusting a black box with legally binding records.

What “legally valid” still requires — even self-hosted

Self-hosting changes where the server runs, not what the law requires. Under ESIGN and UETA an e-signature is enforceable when the process captures the right evidence, so your deployment still needs to record all of it:

  • Intent — a deliberate signing action, not a pre-checked box.
  • Consent to do business electronically — an explicit gate before signing.
  • Association — the signature bound to the exact document version (a hash).
  • Retention & reproducibility — the signed record stored so it can be accurately reproduced.
  • Attribution & audit — who signed, when, and from where.

The full statute-to-code mapping is here: ESIGN & UETA compliance for developers →

The landscape

Self-hostable options, by footprint

All four are open source and Docker-deployable. The practical difference for ops is how much they need underneath.

Languages, licenses, and stars read from the GitHub REST API on 2026-07-23. All four are self-hostable via Docker. Datastore requirements differ — some need an external database, one runs on a single SQLite file; confirm against each project’s current docs.
ToolLanguageLicenseGitHub starsRuns on just a file?
DocuSealRubyAGPL-3.0~18,100No — database-backed
DocumensoTypeScriptAGPL-3.0~14,100No — needs PostgreSQL
OpenSignJavaScriptAGPL-3.0 (core)~6,700No — database-backed
Lifted SignPythonAGPL-3.0~2 (new)Yes — SQLite by default

The biggest operational fork is the datastore. Documenso needs a PostgreSQL instance; DocuSeal and OpenSign are database-backed too. Lifted Sign is the outlier — it boots on a single SQLite file with zero external services, and you point it at Postgres with one environment variable only when you outgrow a file. That makes it the lightest thing to stand up for a proof of concept or a low-volume internal signer.

Run one in about 60 seconds (worked example)

Here is the smallest real deployment — the published Lifted Sign image, one required secret, one port. This is the citable artifact; adapt the same shape for any of the tools above.

# 1. One command — pull the published image and run it.
docker run --rm \
  -e SIGN_SECRET=$(openssl rand -base64 48) \
  -p 8080:8080 \
  ghcr.io/liftedholdings/lifted-sign

# Open http://localhost:8080 — upload a PDF, place fields, send, sign.

Or with Compose, so it restarts and persists the SQLite file:

# docker-compose.yml
services:
  sign:
    image: ghcr.io/liftedholdings/lifted-sign:latest
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:8080"
    environment:
      SIGN_SECRET: "${SIGN_SECRET}"   # required; server refuses to boot without it
      # SMTP so signer links actually get delivered:
      SMTP_HOST: "smtp.example.com"
      SMTP_USER: "postmaster@example.com"
      SMTP_PASS: "${SMTP_PASS}"
      # Outgrew SQLite? point at Postgres with one var:
      # DATABASE_URL: "postgresql://user:pass@db:5432/sign"
    volumes:
      - ./data:/app/state   # the SQLite file + uploaded/sealed PDFs live here
The one required setting. SIGN_SECRET keys every session, signer-access cookie, and one-time code, so the server fails closed if it is missing, too short, or a placeholder. Generate a real one — python -c "import secrets; print(secrets.token_urlsafe(48))" — and store it in your secret manager, not in the image.

TLS, reverse proxy, and SMTP — the part that trips people up

The container is the easy 20 minutes. The real friction is the three things around it: terminating HTTPS, proxying to the app, and getting email delivered so signer links don’t land in spam.

1. Reverse proxy + TLS

Bind the app to localhost and put nginx (or Caddy/Traefik) in front with a real certificate. A minimal nginx server block:

server {
  server_name sign.example.com;
  listen 443 ssl;
  ssl_certificate     /etc/letsencrypt/live/sign.example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/sign.example.com/privkey.pem;

  location / {
    proxy_pass         http://127.0.0.1:8080;
    proxy_set_header   Host $host;
    proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Proto $scheme;   # so the app builds https signer links
    client_max_body_size 25m;                        # PDFs are bigger than the nginx default (1m)
  }
}

Use certbot (Let’s Encrypt) for the cert. The two settings people forget: X-Forwarded-Proto (or the app mints http:// signer links behind your HTTPS proxy) and client_max_body_size (the default 1 MB rejects most real PDFs with a confusing 413).

2. SMTP

Signing is worthless if the invite email never arrives. Use a real transactional sender (Postmark, SES, Mailgun, or your own authenticated relay) and set SPF, DKIM, and DMARC on the sending domain — signer links come from an unfamiliar host and get filtered aggressively without them. Test to a Gmail and an Outlook address before you go live.

Backups, retention, and a court-admissible audit trail

A self-hosted signer owns legally binding records, so treat the data like it. Two things to get right:

  • Back up the datastore AND the sealed files. For a file-based setup (Lifted Sign) that is the SQLite file plus the PDF/state volume; for a database-backed tool it is a pg_dump plus the object store. Snapshot both together so the audit records and the documents they describe stay consistent. Test a restore — an untested backup is a hope, not a backup.
  • Keep the Certificate of Completion with the document. The evidence that makes a signature enforceable — signer identity, timestamps, IP, consent — belongs in the sealed record, not in a log you might rotate away. A PAdES/PKCS#7 signature makes the PDF itself tamper-evident: any later edit breaks the seal visibly, which is exactly what you want to show a court.

Set a retention policy that matches the underlying agreement (many commercial contracts imply 7+ years) and make sure your backup lifecycle doesn’t silently delete records you are legally expected to reproduce.

When not to self-host

Self-hosting a US-law (ESIGN/UETA) e-signature server is the right call for most SMB and internal use. It is the wrong call when you need:

  • Qualified electronic signatures (QES) or eIDAS conformance in the EU — those require a Qualified Trust Service Provider (QTSP) and certificates you cannot mint yourself. Self-hosting doesn’t grant qualified status.
  • Regulated identity proofing (e.g. KYC-grade signer verification) you don’t want to build and maintain.
  • Someone else on the hook for uptime and compliance — a managed provider’s SLA and certifications may be worth more than the control you give up.

A reasonable middle path: self-host for control, but use a managed option (like Lifted Sign’s cloud) when you’d rather not run TLS, SMTP, and backups yourself.

Methodology & sources

How we compiled this

Every license, star count, and language below was read from the GitHub REST API on July 23, 2026; pricing and carrier figures come from each vendor’s own pages on the same date. Open-source status means source published under an OSI/FSF-recognized license you can self-host and inspect. Figures change — check the primary source before you rely on a number, and tell us if anything here is out of date.

  1. Lifted Sign (worked example: SQLite default, PAdES sealing, one-command Docker run) — github.com/LiftedHoldings/lifted-sign; read 2026-07-23.
  2. DocuSeal — github.com/docusealco/docuseal (AGPL-3.0, Ruby, ~18,100★; read 2026-07-23).
  3. Documenso — github.com/documenso/documenso (AGPL-3.0, TypeScript, PostgreSQL-backed, ~14,100★; read 2026-07-23).
  4. OpenSign — github.com/OpenSignLabs/OpenSign (AGPL-3.0 core, JavaScript, ~6,700★; read 2026-07-23).
  5. ESIGN Act — 15 U.S.C. §7001, law.cornell.edu/uscode/text/15/7001.
FAQ

Common questions

Can I self-host an e-signature server for free?
Yes. DocuSeal, Documenso, OpenSign, and Lifted Sign are all open source (AGPL-3.0) and can be self-hosted at no license cost — you pay only for your own infrastructure. Lifted Sign is the lightest to stand up because it runs on a single SQLite file with no external database.
Do I need a database to self-host e-signatures?
It depends on the tool. Documenso requires PostgreSQL, and DocuSeal and OpenSign are database-backed. Lifted Sign runs on SQLite by default with no external database, and can be pointed at PostgreSQL with one environment variable when you outgrow a file.
Is a self-hosted e-signature legally binding?
Yes, in the US, provided your deployment captures the evidence ESIGN and UETA require: intent, consent to sign electronically, association of the signature with the document (a hash), a retained reproducible record, and an attribution/audit trail. Self-hosting does not change the legal requirements — only where the server runs.
What is the hardest part of self-hosting an e-signature server?
Not the container — the pieces around it. Terminating TLS and reverse-proxying correctly (forward X-Forwarded-Proto or you get http links, and raise client_max_body_size or PDFs 413), and getting SMTP deliverability right with SPF/DKIM/DMARC so signer invitations do not land in spam.
When should I use a managed e-signature service instead?
When you need EU qualified signatures (QES/eIDAS), which require a Qualified Trust Service Provider you cannot self-host; when you want regulated identity proofing you would rather not build; or when a provider carrying uptime and compliance obligations under an SLA is worth more than the control you give up.
Open source, honestly

Stand up your own signer in one command.

Lifted Sign self-hosts free under AGPL-3.0 on nothing but SQLite — or skip the TLS-and-SMTP chores and run the managed cloud.