Espial

June 8, 2026 ยท View on GitHub

This repository contains the instruction and commands for running Espial from the pre-built docker images on Docker Hub.

Espial

Espial is an open-source, web-based bookmarking server.

It allows mutiple accounts, but currently intended for self-host scenarios.

The bookmarks are stored in a sqlite3 database, for ease of deployment & maintenence.

The easist way for logged-in users to add bookmarks, is with the "bookmarklet", found on the Settings page.

Source Repository

https://github.com/jonschoning/espial

Running the pre-built Docker Hub Image (POSIX Only)

  • These commands use docker compose with settings in docker-compose.yml
  • see Makefile for additional commands
  1. Clone this repository
git clone https://github.com/jonschoning/espial-docker
cd espial-docker
  1. Pull the image from Docker Hub
make pull
  1. Start Espial
./espial-svc-start
  • When the app starts, it should create the DB espial.sqlite3 in the current directory (or according to docker-compose.yml)
  1. Create a user
docker compose exec espial ./migration createuser --userName myusername --userPassword myuserpassword
  • see docker compose exec espial ./migration for all available cli commands
  • use the /app/data/ file prefix to access files on the host according to the bind mount volume inside the container, see docker-compose.yml
  1. Import a pinboard bookmark file for a user (optional)
docker compose exec espial ./migration importbookmarks --userName myusername --bookmarkFile /app/data/sample-bookmarks.json
  1. Import a firefox bookmark file for a user (optional)
docker compose exec espial ./migration importfirefoxbookmarks --userName myusername --bookmarkFile /app/data/firefox-bookmarks.json
  1. Stop Espial
./espial-svc-stop

SystemD Service

copy this repo to /opt/espial/

a serivce unit is provided at etc/systemd/system/espial.service

which references:

  • espial-svc-start
  • espial-svc-stop

adjust espial-svc-start to control where logs are stored.

Base Image

jonschoning/espial:espial

is based on

gcr.io/distroless/base-debian12

Defaults

As specified in docker-compose.yml:

  • internal app port 3000 is exposed to port 80 on host
  • adjust as necessary

Request IP Logging

Espial supports the IP_FROM_HEADER environment variable for request logging.

  • IP_FROM_HEADER=true: log the client IP from the X-Real-IP or X-Forwarded-For header when present, and fall back to the peer address if neither header is available.
  • IP_FROM_HEADER=false: log the peer address from the HTTP connection.

Only set IP_FROM_HEADER=true if your application is safely positioned behind a trusted reverse proxy.

SSL / Reverse Proxy

Espial does not terminate TLS itself. Run it behind a reverse proxy that handles HTTPS and forwards traffic to Espial over HTTP.

For container-based deployment examples, including production-oriented layouts, see the espial-docker repository:

Minimal Caddy example:

Localhost without a real domain:

https://localhost:3050 {
    reverse_proxy localhost:3000
}

or with a domain:

espial.example.com {
  reverse_proxy 127.0.0.1:3000
}

With the domain setup:

  • Caddy terminates TLS for espial.example.com.
  • Espial continues listening on HTTP, locally on 127.0.0.1:3000
    • If using Docker Compose, it would like like espial:3000
  • Set IP_FROM_HEADER=true only when Espial is reachable solely through that trusted proxy.

If you are using Cloudflare:

  • Prefer Cloudflare SSL mode Full (strict).
  • use header_up X-Forwarded-For {http.request.header.CF-Connecting-IP}
  • If traffic can reach Espial directly without passing through your trusted proxy, do not enable IP_FROM_HEADER=true, because client IP headers can be spoofed.