Skip to content

karloscodes/formlander

Repository files navigation

Formlander

GitHub stars License Docker Pulls

Form backend you actually own. Collect submissions on your server. One Docker command. No SaaS bills.

Website · Docs · Deploy

Overview

Formlander enables developers running static or serverless sites to handle form submissions with a single-binary deployment. Store submissions in SQLite, review them in a lightweight admin UI, and route data asynchronously to webhooks or email via Mailgun.

Features

  • Single-binary deployment — One executable with SQLite storage, no external dependencies
  • Admin dashboard — View and manage forms, submissions, and delivery status
  • Asynchronous delivery — Queue webhook and email notifications with retry logic
  • Spam protection — Configurable honeypot fields and rate limiting
  • API-first design — Dashboard consumes the same REST endpoints available for integrations
  • Your server, your data — We don't run servers. We can't see your submissions. That's the point.

JavaScript SDK (Optional)

For improved reliability, include the optional SDK that adds automatic retry with exponential backoff:

<script src="https://your-formlander.com/assets/formlander.js"></script>

<form action="https://your-formlander.com/forms/contact/submit?token=YOUR_TOKEN" method="post">
  <input name="email" type="email" required>
  <button type="submit">Send</button>
</form>

The SDK auto-detects Formlander forms and enhances them with:

  • Retry logic — 3 attempts with exponential backoff on 503/network errors
  • Graceful degradation — Falls back to normal form POST if JS fails
  • Loading states — Disables form and shows "Sending..." during submission

Forms work without the SDK via standard HTML POST. The SDK is purely an enhancement.

Installation

One-Line Install (Recommended for VPS/Servers)

Install Formlander with Docker, Caddy reverse proxy, and automatic SSL certificates:

curl -fsSL https://raw.githubusercontent.com/karloscodes/formlander/master/install.sh | sudo bash

This interactive installer will:

  • Check system requirements (Docker, ports 80/443)
  • Prompt for your domain name
  • Set up Caddy as a reverse proxy with automatic HTTPS
  • Configure automatic daily backups
  • Start the Formlander container

After installation, access your dashboard at https://your-domain.com

Management commands:

formlander update              # Update to latest version
formlander reload              # Reload containers
formlander restore-db          # Restore from backup
formlander change-admin-password  # Reset admin password

Quick Start (Development/Local)

Using Docker

First, generate and save your session secret:

# Generate once and save this value securely
export FORMLANDER_SESSION_SECRET=$(openssl rand -hex 32)
echo "Save this secret: $FORMLANDER_SESSION_SECRET"

Then run the container with your saved secret:

docker run -d \
  -p 8080:8080 \
  -e FORMLANDER_SESSION_SECRET="your-saved-secret-here" \
  -v $(pwd)/storage:/app/storage \
  karloscodes/formlander:latest

Important: Use the same FORMLANDER_SESSION_SECRET value across restarts to prevent logging out all users.

HTTPS is required. The Docker image runs in production mode, which marks the session cookie Secure. Without TLS in front (Caddy, Nginx, Traefik, etc.) browsers silently drop the cookie and login appears to fail. The bundled install.sh sets up Caddy with automatic certificates; if you roll your own with docker-compose, put a TLS terminator in front of :8080.

Access the admin dashboard at http://localhost:8080 with default credentials:

  • Email: admin@formlander.local
  • Password: formlander (you'll be prompted to change this on first login)

Running the Binary

  1. Download the latest release from the Releases page
  2. Generate and save your session secret:
    # Generate once and save this value
    export FORMLANDER_SESSION_SECRET=$(openssl rand -hex 32)
    echo "Save this secret: $FORMLANDER_SESSION_SECRET"
    
    export FORMLANDER_DATA_DIR=./storage
  3. Run the binary:
    ./formlander

Configuration

Formlander uses Viper for flexible configuration. You can configure via:

  • Environment variables (prefix: FORMLANDER_)
  • .env file for easier local development
  • Environment variables always override .env file values

Required Environment Variable (Production Only):

  • FORMLANDER_SESSION_SECRET - HMAC secret for signing session cookies (fixed default in dev/test)

Optional Environment Variables:

  • FORMLANDER_ENV - Environment mode: development, production (default: development for binary / go run; the Docker image sets production)
  • FORMLANDER_PORT - HTTP port (default: 8080)
  • FORMLANDER_LOG_LEVEL - Log level: debug, info, warn, error (default: error)
  • FORMLANDER_DATA_DIR - Data directory path (default: ./storage)

Note: In development/test, a fixed default secret is used if not set, allowing sessions to persist across restarts.

HTTPS required when setting FORMLANDER_ENV=production. Production mode marks the session cookie Secure, so browsers will not send it back over plain HTTP and login will appear to silently fail. Terminate TLS in front of Formlander (the bundled install.sh does this with Caddy automatically). The default is development, which is safe for plain-HTTP local testing.

Or use a .env file (.env):

FORMLANDER_ENV=production
FORMLANDER_PORT=8080
FORMLANDER_SESSION_SECRET=your-secret-here
FORMLANDER_LOG_LEVEL=info
FORMLANDER_DATA_DIR=./storage

Building from Source

  1. Clone the repository
  2. Build:
    make build
  3. Generate and save your session secret, then run:
    # Generate once and save this value
    export FORMLANDER_SESSION_SECRET=$(openssl rand -hex 32)
    echo "Save this secret: $FORMLANDER_SESSION_SECRET"
    
    ./bin/formlander

Releases

Formlander uses semantic versioning. Docker images are published via GitHub Releases when version tags are pushed.

Docker Images

# Latest stable release
docker pull karloscodes/formlander:latest

# Specific version
docker pull karloscodes/formlander:v1.0.0

# Major version (receives minor + patch updates)
docker pull karloscodes/formlander:v1

Note: Docker images are published automatically via GitHub Actions when a version tag (e.g., v1.0.0) is pushed to the repository.

Building from Source

If you prefer to run a native binary instead of Docker:

git clone https://github.com/karloscodes/formlander.git
cd formlander
make build
export FORMLANDER_SESSION_SECRET=$(openssl rand -hex 32)
./bin/formlander

Supported platforms for building from source:

  • Linux (amd64, arm64)
  • macOS (amd64, arm64)

Architecture

Formlander follows a Phoenix Context Architecture, organizing code into bounded contexts with clear separation of concerns:

[Static Site] --> POST /forms/:slug/submit
                        |
                  [Formlander]
                   /    |    \
            [SQLite] [Jobs] [Admin UI]
                      |
               [Webhook/Email Dispatchers]
                      |
            [External Services/Mailgun]

Key Components

  • HTTP Server — Fiber-based cartridge wrapper handling public submissions and admin dashboard
  • Database Layer — GORM + SQLite with WAL mode
  • Custom Write Retry Logicdbtxn.WithRetry ensures writes eventually succeed despite SQLite's single-writer constraint
  • Job System — In-process dispatchers for asynchronous webhook and email delivery
  • Cartridge Context — Request-scoped dependency injection providing type-safe access to logger, config, and database

SQLite Write Handling

Due to SQLite's single-writer limitation, all write operations use a custom retry mechanism (internal/pkg/dbtxn/retry.go) that:

  • Detects busy/locked database errors
  • Retries with exponential backoff (up to 10 attempts)
  • Adds jitter to prevent thundering herd issues
  • Works alongside WAL mode, busy_timeout pragmas, and immediate transaction locks

This ensures writes eventually succeed even under concurrent load.

Development

Build and run locally:

make build
make dev

Run tests:

make test

Contributing

Contributions are welcome! Please open an issue first to discuss proposed changes, or submit a pull request for bug fixes and improvements.

License

Formlander License Agreement — see LICENSE file for details.

About

Form backend for static sites

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors