Docker Deployment Guide
View SourceThis guide explains how to deploy the Cryptic client and or the server using Docker and Docker Compose.
Quick Deployment
Video demos:
Fetch the Docker images
Start by fetching the Docker images:
# Get the latest Client Docker image
docker pull ghcr.io/etnt/cryptic-tui:latest
# Get the latest Server Docker image
# (only necessary of you want to run the Cryptic server)
docker pull ghcr.io/etnt/cryptic:latest
Run the Client container
Run the latest client image:
# STEP 1: Create a separate GPG directory for Docker
mkdir -p ~/.cryptic-gpg
# STEP 2: Run the Cryptic Onboarding script
# 1. Generate a GPG key pair (stored in ~/.cryptic-gpg on Host)
# 2. Export the generated key
# 3. Send the key (and fingerprint) to the admin
# 4. When admin has registered your key:
# - Request a TLS certificate from server
# - If your server is running on localhost on your Host machine, specify:
# cryptic-server as your server address (see the: `--add-host` below)
# 5. Exit the onboard script
#
# You should now see your certificates at ~/.cryptic/<user>/cryptic-server_<port>
#
# NOTE: We use ~/.cryptic-gpg instead of ~/.gnupg because modern macOS/Linux
# GPG uses keyboxd daemon which doesn't work across container boundaries.
# The container manages its own GPG keyring that persists on the host.
docker run -it --rm --name cryptic-client \
-v ~/.cryptic:/home/cryptic/.cryptic \
-v ~/.cryptic-gpg:/home/cryptic/.gnupg \
--add-host=cryptic-server:host-gateway \
ghcr.io/etnt/cryptic-tui:latest sh -c 'cryptic --onboard'
# Start the Cryptic client with your username (e.g `bob`)
# - You'll be prompted for a Passphrase which is used to encrypt your local DB
# - Here we also increase the log output details
# - The incoming sender's username will be written to the file: sender.msg
docker run -it --rm --name cryptic-client \
-v ~/.cryptic:/home/cryptic/.cryptic \
-v ~/.cryptic-gpg:/home/cryptic/.gnupg \
--add-host=cryptic-server:host-gateway \
-e CRYPTIC_DEBUG=true \
ghcr.io/etnt/cryptic-tui:latest \
sh -c 'cryptic -u bob --enable-db --file-notify sender.msg --tui'
# To get notified of incoming messages, run (on MAC):
# - brew install fswatch terminal-notifier
fswatch -0 ~/.cryptic/bob/cryptic-server_8443/sender.msg | xargs -0 -n1 -I{} $HOME/.cryptic/notify_script.sh {}
# Content of notification script
> cat $HOME/.cryptic/notify_script.sh
#!/bin/bash
terminal-notifier -title "Cryptic" -message "Cryptic message from: $1" -sound Pong
Run the Server container
Run the latest server image:
# STEP 1: Create a directory for storing all Cryptic server data
mkdir -p ~/.cryptic_server
cd ~/.cryptic_server
# STEP 2: Generate CA and server certificates (one-time setup)
# This will prompt for optional DNS Subject Alternative Names (SANs)
docker run -it --rm \
--entrypoint '' \
-v $(pwd):/opt/cryptic/server_data \
-e CRYPTIC_SERVER_DIR=/opt/cryptic/server_data \
ghcr.io/etnt/cryptic:latest \
sh -c 'DIR="${CRYPTIC_SERVER_DIR}/priv/ssl" generate-mtls-certs.sh'
# STEP 3: Bootstrap the first admin user (one-time setup)
# Generate and Export a GPG key for the admin user
# Take note of the name of the exported filename
mkdir -p ~/.cryptic ~/.cryptic-gpg
docker run -it --rm --name cryptic-client \
-v ~/.cryptic:/home/cryptic/.cryptic \
-v ~/.cryptic-gpg:/home/cryptic/.gnupg \
--add-host=cryptic-server:host-gateway \
ghcr.io/etnt/cryptic-tui:latest sh -c 'cryptic --onboard'
# STEP 4: Copy the exported GPG key where Cryptic will find it
mkdir -p ~/.cryptic_server/priv/ca/bootstrap
cp ~/.cryptic/gpg-export/<filename> ~/.cryptic_server/priv/ca/bootstrap/admin.gpg
# STEP 5: Run the server
# For debug log output, add: -e CRYPTIC_DEBUG=true
docker run -d \
--name cryptic-server \
-p 8443:8443 \
-v $(pwd):/opt/cryptic/server_data:rw \
-e CRYPTIC_SERVER_HOST=0.0.0.0 \
-e CRYPTIC_SERVER_DIR=/opt/cryptic/server_data \
ghcr.io/etnt/cryptic:latest
# STEP 6: Connect (login) the admin user
# See `Run the Client container` above
# Check the server logs
docker logs cryptic-server
tail -f ./logs/server.log
# Stop the server and remove the container
docker stop cryptic-server
docker rm cryptic-server
# Remove all server data
rm -rf ~/.cryptic_server
# Remove all GPG keys
rm -rf ~/.cryptic-gpg
# Remove all user data
rm -rf ~/.cryptic
Manual Certificate Generation (Optional)
The Quick Deployment section above includes certificate generation as Step 1. If you need to regenerate certificates or add additional DNS SANs later:
cd ~/.cryptic_server
# Run certificate generation interactively (will prompt for DNS SANs)
docker run -it --rm \
--entrypoint '' \
-v $(pwd):/opt/cryptic/server_data \
-e CRYPTIC_SERVER_DIR=/opt/cryptic/server_data \
ghcr.io/etnt/cryptic:latest \
sh -c 'DIR="${CRYPTIC_SERVER_DIR}/priv/ssl" generate-mtls-certs.sh'
The Client
The Cryptic TUI (Terminal User Interface) client can run in Docker to connect to a Cryptic server. This approach provides a consistent, containerized environment for the client without requiring local Erlang/Rust installation.
Architecture Overview
The Docker TUI client uses the existing bin/cryptic --tui script,
which automatically:
- Starts an Erlang backend node.
- Launches the Rust TUI binary.
- Manages lifecycle, authentication, and graceful shutdown.
┌─────────────────────────────────────────┐
│ Docker Container: cryptic-tui │
│ │
│ bin/cryptic --tui │
│ ├─> Erlang Node (backend) │
│ │ ├─ cryptic_engine │
│ │ ├─ cryptic_ws_client │
│ │ └─ cryptic_tui_bridge │
│ │ │
│ └─> Rust TUI (frontend) │
│ └─ Connects via dist Erlang │
└─────────────────────────────────────────┘
│
│ WebSocket mTLS (over network/internet)
▼
┌─────────────────────────────────────────┐
│ Cryptic Server (managed elsewhere) │
│ (WebSocket server on port 8443) │
└─────────────────────────────────────────┘Prerequisites
Docker (version 20.10 or later)
docker --versionDocker Compose (version 2.0 or later)
docker compose versionUser Identity Setup - You need to set up your Cryptic identity first. You can run the onboarding process from within the Docker container:
docker compose run --rm cryptic-tui sh -c "cryptic --onboard"This creates your certificates and keys in
~/.cryptic/<username>/<server>_<port>/cryptic-tui Repository (temporary requirement) - The Docker build requires the
cryptic-tuirepository to be available as a sibling directory:parent-directory/ ├── cryptic/ ← This repository └── cryptic-tui/ ← Clone of git@github.com:etnt/cryptic-tui.gitClone it:
cd /path/to/your/projects git clone git@github.com:etnt/cryptic-tui.gitNote: Once
cryptic-tuiis publicly released on GitHub, the Dockerfile will be updated to clone it automatically, eliminating this manual step.
Quick Start
Ensure cryptic-tui is cloned (see Prerequisites above):
ls ../cryptic-tui/ # Should show the cryptic-tui repositoryBuild the Docker image:
docker compose build cryptic-tuiConnect to a server:
CRYPTIC_USERNAME=alice \ CRYPTIC_SERVER_HOST=relay.example.com \ CRYPTIC_SERVER_PORT=8443 \ CRYPTIC_ENABLE_DB=true \ docker compose run --rm cryptic-tuiFor local development (connecting to localhost server):
CRYPTIC_USERNAME=alice \ CRYPTIC_SERVER_HOST=cryptic-server \ CRYPTIC_ENABLE_DB=true \ docker compose run --rm cryptic-tui
Configuration
Environment Variables
Configure the client using these environment variables (mapped to bin/cryptic script options):
| Docker Env Variable | Script Option | Default | Description |
|---|---|---|---|
CRYPTIC_USERNAME | -u, --username | alice | Your username |
CRYPTIC_SERVER_HOST | -s, --server-host | localhost | Server hostname |
CRYPTIC_SERVER_PORT | -p, --server-port | 8443 | Server port |
CRYPTIC_NODE_NAME | --name | localhost | Erlang node hostname |
CRYPTIC_ENABLE_DB | --enable-db | false | Enable message history |
CRYPTIC_DEBUG | (internal flag) | false | Enable verbose debug logging |
ERLANG_COOKIE | (Erlang cookie) | (auto-generated) | Erlang distributed cookie for node authentication |
Note on Erlang Cookie: If not provided, the entrypoint script will generate a random cookie and display it. For connecting to other Erlang nodes, you must set the same ERLANG_COOKIE value across all nodes.
Example with custom settings:
Volume Mounts - Critical for Persistence
The container mounts the entire ~/.cryptic directory (read-write):
volumes:
- ~/.cryptic:/home/cryptic/.crypticNote: The .erlang.cookie file is NOT mounted from the host. It's automatically generated inside the container by the entrypoint script and is only used for intra-container distributed Erlang communication between the Erlang backend node and the Rust TUI process.
This directory contains all persistent storage:
- Certificates:
<username>/<server>_<port>/certificates/*.{crt,key} - Identity Keys:
<username>/<server>_<port>/keys.encrypted(X3DH keys) - Session States:
<username>/<server>_<port>/sessions/*.session(Double Ratchet) - Message Database:
<username>/messages.db(if--enable-db) - Logs:
logs/cryptic-tui.log.*
Why read-write? The Erlang backend needs to:
- Load and save encrypted identity keys
- Update Double Ratchet session states after each message
- Write to SQLite database (if message history enabled)
- Append to log files
Permission note:
- On macOS/Windows Docker Desktop, file permissions work automatically
- On Linux, ensure your user owns
~/.cryptic/or the container's UID (100) can write to it
Docker Image Details
Multi-Stage Build
The Dockerfile.tui uses three stages:
Rust builder stage (based on
rust:1.83-alpine):- Compiles the Rust TUI binary from
../cryptic-tui/ - Produces a statically linked executable
- Compiles the Rust TUI binary from
Erlang builder stage (based on
erlang:28.1-alpine):- Installs rebar3
- Compiles the Erlang application
- Builds the
cryptic_nif.sonative library with libsodium - Creates a production release
Runtime stage (based on
alpine:latest):- Minimal base image with OpenSSL, libsodium, and ncurses
- Non-root user (
cryptic:cryptic) - Includes both Erlang release and Rust TUI binary
- Configured for interactive terminal mode
- Uses
docker-tui-entrypoint.shfor initialization
The entrypoint script (docker-tui-entrypoint.sh) handles:
- Fixing permissions on mounted
~/.crypticdirectory - Setting up or generating Erlang cookie (
.erlang.cookie) - Checking for required certificates (with helpful error messages)
- Displaying connection information
- Launching
cryptic --tuiwith proper environment
Key Design Decisions
- Entrypoint script: Uses
docker-tui-entrypoint.shfor setup (permission fixes, Erlang cookie, certificate checks) - Reuse existing launcher: Calls
cryptic --tuifor actual TUI launch - Environment over args: Maps script flags to Docker environment variables
- Standard structure: Follows same directory layout as local installation
- Server hostname flexibility: Can connect to any server (local or remote)
- Interactive mode: Requires
stdin_open: trueandtty: truefor terminal UI - Client-only focus: Client doesn't need to build or manage the server
- User-friendly feedback: Provides helpful error messages and connection info on startup
Common Operations
Building the Image
Important: The Dockerfile expects to be run from the parent directory containing both cryptic/ and cryptic-tui/ as subdirectories.
Build the Docker image:
# From the parent directory (containing both cryptic/ and cryptic-tui/)
cd /path/to/parent-directory
docker build -t cryptic-tui:latest -f cryptic/Dockerfile.tui .
Or using Docker Compose (handles build context automatically):
# From the cryptic directory
docker compose build cryptic-tui
Build without cache (fresh build):
docker compose build --no-cache cryptic-tui
Common Error: If you get COPY failed: file not found errors, ensure you're building from the parent directory, not from within cryptic/:
# Wrong (from inside cryptic/)
docker build -t cryptic-tui:latest -f Dockerfile.tui . # ❌ Will fail
# Correct (from parent directory)
cd ..
docker build -t cryptic-tui:latest -f cryptic/Dockerfile.tui . # ✅ Works
Running the Client
Run interactively (recommended):
CRYPTIC_USERNAME=alice docker compose run --rm cryptic-tui
Run with custom server:
CRYPTIC_USERNAME=alice \
CRYPTIC_SERVER_HOST=relay.example.com \
docker compose run --rm cryptic-tui
Enable message history:
CRYPTIC_USERNAME=alice \
CRYPTIC_ENABLE_DB=true \
docker compose run --rm cryptic-tui
Accessing the Container
Open a shell in the container (for debugging):
docker compose run --rm cryptic-tui /bin/sh
Run the client manually (inside container):
docker compose run --rm cryptic-tui /bin/sh
# Inside container:
cryptic --tui -u alice -s cryptic-server -p 8443
Run in console mode (no TUI):
docker compose run --rm cryptic-tui cryptic -u alice -s cryptic-server
Run the onboarding script (no TUI):
docker compose run --rm cryptic-tui cryptic --onboard
NOTE When running the onboarding script with --onboard , then you have to continue through all steps 1-3, i.e generate GPG keys (1), export your key (2) and send it to the administrator of the server, request a certificate (3) (when registered). Else, you will loose your GPG key if you exit the container half-way through and you have to repeat the process again. Not until your certificate has been received will it be stored on your shared ~/.cryptic volume.
Viewing Logs
Check TUI logs (from host):
tail -f ~/.cryptic/logs/cryptic-tui.log.*
Inside container:
docker compose run --rm cryptic-tui /bin/sh
tail -f /home/cryptic/.cryptic/logs/cryptic-tui.log.*
Check cryptic client logs (from host):
tail -f ~/.cryptic/<username>/<server>_<port>/logs/cryptic-tui.log.*
Networking
Connecting to Host Server (Development)
When the Cryptic server runs on your host machine:
CRYPTIC_SERVER_HOST=cryptic-server docker compose run --rm cryptic-tui
The mapping is done in extra_hosts in docker-compose.yml:
services:
cryptic-tui:
extra_hosts:
- "cryptic-server:host-gateway"NOTE The Cryptic server has a SAN extension: DNS: cryptic-server in its certificate.
Connecting to Another Container
If the server runs in Docker on the same network:
CRYPTIC_SERVER_HOST=cryptic-server docker compose run --rm cryptic-tui
Ensure both services are on the same network:
services:
cryptic-server:
networks:
- cryptic-network
cryptic-tui:
networks:
- cryptic-network
networks:
cryptic-network:
driver: bridgeConnecting to Remote Server
For production/remote servers:
CRYPTIC_USERNAME=alice \
CRYPTIC_SERVER_HOST=relay.example.com \
CRYPTIC_SERVER_PORT=8443 \
docker compose run --rm cryptic-tui
Troubleshooting
Build Failures
Error: "COPY failed: file not found in build context"
This means cryptic-tui is not in the expected location.
NOTE FIXME - this is just temporary until the repo is public
Solution:
# Check current location
pwd
# Should be inside the cryptic directory
# Check parent directory
ls ../
# Should show cryptic-tui directory
# If not, clone it:
cd ..
git clone git@github.com:etnt/cryptic-tui.git
cd cryptic
Error: "failed to compute cache key: too many links"
This is a macOS Docker issue with symlinks (resolved in current Dockerfile).
If you still encounter it:
# Clean Docker build cache
docker builder prune -a
docker compose build --no-cache cryptic-tui
Error: "error getting credentials - docker-credential-desktop not found"
Temporary fix for macOS Docker Desktop:
# Backup config
cp ~/.docker/config.json ~/.docker/config.json.backup
# Remove problematic credsStore setting
jq 'del(.credsStore)' ~/.docker/config.json > ~/.docker/config.json.tmp
mv ~/.docker/config.json.tmp ~/.docker/config.json
# Try build again
docker compose build cryptic-tui
Connection Issues
Cannot connect to server
Check environment variables:
docker compose run --rm cryptic-tui env | grep CRYPTIC
Verify server is reachable from container:
docker compose run --rm cryptic-tui ping cryptic-server
# or
docker compose run --rm cryptic-tui nc -zv cryptic-server 8443
TUI starts but connection fails
Check certificates exist:
ls -la ~/.cryptic/alice/localhost_8443/certificates/
# Should show: alice.crt, alice.key, ca.crt
If certificates are missing, the entrypoint script will display a warning with instructions. Run onboarding:
docker compose run --rm cryptic-tui sh -c "cryptic --onboard"
Verify server host setting:
docker compose run --rm cryptic-tui env | grep CRYPTIC_SERVER_HOST
Debug mode
Enable verbose logging:
CRYPTIC_DEBUG=true docker compose run --rm cryptic-tui
# Then check logs:
tail -f ~/.cryptic/logs/cryptic-tui.log.*
Terminal Display Issues
Terminal size incorrect
Ensure TTY is enabled:
services:
cryptic-tui:
stdin_open: true
tty: trueColors not working
Set terminal type:
TERM=xterm-256color docker compose run --rm cryptic-tui
TUI not responding to input
Check if stdin is properly attached:
# Use -it flags explicitly
docker compose run -it --rm cryptic-tui
Permission Issues (Linux)
If you get permission errors on ~/.cryptic/:
# Check ownership
ls -ld ~/.cryptic
# Fix ownership (replace 1000:1000 with your UID:GID)
sudo chown -R 1000:1000 ~/.cryptic
# Or set permissions
chmod -R u+rwX ~/.cryptic
Production Deployment
Security Considerations
- Certificate management: Ensure certificates are properly protected
- Volume permissions: Set strict permissions on
~/.cryptic/(0700) - Network isolation: Use Docker networks to isolate client traffic
- Regular updates: Keep the base image and dependencies updated
- Debug logging: Disable
CRYPTIC_DEBUGin production
Recommended Setup
Use secrets for sensitive data:
services: cryptic-tui: secrets: - user_cert - user_key secrets: user_cert: file: ~/.cryptic/alice/localhost_8443/certificates/alice.crt user_key: file: ~/.cryptic/alice/localhost_8443/certificates/alice.keySet resource limits:
services: cryptic-tui: deploy: resources: limits: cpus: '1.0' memory: 512M reservations: cpus: '0.5' memory: 256MUse specific image tags:
services: cryptic-tui: image: cryptic-tui:1.0.0 # Don't use :latest in production
Advanced Usage
Running Without Docker Compose
Run the container directly:
docker run -it --rm \
--name cryptic-tui \
-v ~/.cryptic:/home/cryptic/.cryptic \
-e CRYPTIC_USERNAME=alice \
-e CRYPTIC_SERVER_HOST=relay.example.com \
-e CRYPTIC_SERVER_PORT=8443 \
-e CRYPTIC_ENABLE_DB=false \
cryptic-tui
Building for Different Architectures
Build for ARM64 (Apple Silicon):
docker buildx build -f Dockerfile.tui --platform linux/arm64 -t cryptic-tui:arm64 .
Build multi-architecture image:
docker buildx build -f Dockerfile.tui --platform linux/amd64,linux/arm64 -t cryptic-tui:latest .
Custom Build with Local cryptic-tui Changes
If you're developing cryptic-tui:
# Make changes to ../cryptic-tui/
# Then rebuild:
docker compose build --no-cache cryptic-tui
# Test your changes:
CRYPTIC_USERNAME=alice docker compose run --rm cryptic-tui
Future Improvements
Once the cryptic-tui repository is public, the Dockerfile will be updated to:
# Future version (no manual clone needed)
RUN git clone https://github.com/etnt/cryptic-tui.git /cryptic-tuiThis will eliminate the need for manual cloning and sibling directory setup.
The Server
The Cryptic server Docker deployment uses a multi-stage build process to create a minimal, secure container image. The image runs the Erlang release in foreground mode and requires external mTLS certificates mounted as volumes.
Prerequisites
Docker (version 20.10 or later)
docker --versionDocker Compose (version 2.0 or later)
docker compose versionmTLS Certificates - You need:
- Server certificate (
server.crt) - Server private key (
server.key) - CA certificate (
ca.crt)
See Certificate Generation below for instructions.
- Server certificate (
Quick Start
Generate certificates (if you haven't already):
./scripts/generate-mtls-certs.shBuild the Docker image:
docker build -t cryptic-server .Start the server using Docker Compose:
docker compose up -dCheck server status:
docker compose ps docker compose logs -f cryptic-serverConnect a client (from the host):
cryptic_console ...Study the server.log:
# Tail the log in real-time docker exec cryptic-server tail -f /opt/cryptic/logs/server.log # Copy the log to your host docker cp cryptic-server:/opt/cryptic/logs/server.log ./server.log # Interactive shell docker exec -it cryptic-server /bin/sh # Then you can use: cd /opt/cryptic/logs && ls -la # And: cat server.log, tail -f server.log, etc.Get a remote Erlang shell:
docker exec -it cryptic-server bin/cryptic remote_console
Docker Image Details
Multi-Stage Build
The Dockerfile uses two stages:
Builder stage (based on
erlang:28.1-alpine):- Installs rebar3
- Compiles the Erlang application
- Builds the cryptic_nif.so native library with libsodium
- Creates a production release
Runtime stage (based on
alpine:latest):- Minimal base image with OpenSSL and libsodium
- Non-root user (
cryptic:cryptic) - Only includes the compiled release
- Health check on port 8443
Image Size
The final image is approximately 38-40 MB, significantly smaller than including the full Erlang/OTP development environment.
Configuration
Environment Variables
Configure the server using these environment variables (names reflect the Erlang server code in cryptic_server.erl and cryptic_ca_app.erl):
| Variable | Default | Description |
|---|---|---|
CRYPTIC_SERVER_HOST | 0.0.0.0 | Server bind address (use 0.0.0.0 for Docker) |
CRYPTIC_SERVER_PORT | 8443 | WebSocket server port |
CRYPTIC_SERVER_CERT | /opt/cryptic/certs/server.crt | Server certificate path (mTLS) |
CRYPTIC_SERVER_KEY | /opt/cryptic/certs/server.key | Server private key path |
CRYPTIC_CA_CERT | /opt/cryptic/certs/ca.crt | CA certificate used to verify client certs |
CRYPTIC_CA_DB_FILE | /opt/cryptic/data/ca/cryptic_ca.db | CA database (stores user registrations, fingerprints, issuance metadata) |
CRYPTIC_EVENT_HANDLERS | cryptic_file_logger | Comma-separated event handlers (logging) |
CRYPTIC_DEBUG | (unset) | Set to "true" to enable verbose debug logging in event handlers |
You can override these in docker-compose.yml:
services:
cryptic-server:
environment:
- CRYPTIC_SERVER_PORT=9443
- CRYPTIC_SERVER_HOST=0.0.0.0
- CRYPTIC_DEBUG=trueVolume Mounts
The docker-compose.yml defines several volumes:
Certificate volumes (read-only):
volumes: - ./priv/ssl/server.crt:/opt/cryptic/certs/server.crt:ro - ./priv/ssl/server.key:/opt/cryptic/certs/server.key:ro - ./priv/ssl/ca.crt:/opt/cryptic/certs/ca.crt:roData volumes (persistent):
volumes: - cryptic-logs:/opt/cryptic/logs - cryptic-data:/opt/cryptic/dataOptional CA bootstrap (GPG fingerprints): If you want to preload verified user fingerprints, place
.gpgfiles inpriv/ca/bootstrap/before building the image, or mount a host directory into the releaseprivpath after deployment. During build, the release copies yourpriv/tree, so any files underpriv/ca/bootstrapbecome available at runtime.Example (build-time approach):
# Add files like priv/ca/bootstrap/alice.gpg, bob.gpg docker build -t cryptic-server .Example (runtime mount — adjust version number after inspecting container):
# Discover priv dir inside container docker compose exec cryptic-server bin/cryptic eval 'io:format("~s\n", [code:priv_dir(cryptic)]).' # Suppose output is /opt/cryptic/lib/cryptic-1.2.3/priv # Then add to docker-compose.yml: volumes: - ./bootstrap:/opt/cryptic/lib/cryptic-1.2.3/priv/ca/bootstrap:ro
Port Mapping
The default configuration maps port 8443 from the container to the host:
ports:
- "8443:8443"To use a different host port (e.g., 9443):
ports:
- "9443:8443"Certificate & CA Database Generation
Generate the CA and Server certificates using the
scripts/generate-mtls-certs.sh script.
Production Note: For production deployments, use certificates from a
trusted Certificate Authority. The CA database (CRYPTIC_CA_DB_FILE) is persisted on a volume; back it up regularly.
CA Database Persistence
The CA subsystem stores state (user registrations, issued cert metadata) in the SQLite file referenced by CRYPTIC_CA_DB_FILE. Mount the parent directory (/opt/cryptic/data/ca) as a named volume or host bind to retain state across container restarts:
volumes:
- cryptic-ca-data:/opt/cryptic/data/caThe server does not store end-to-end encrypted chat messages; those are only persisted client-side in each user's messages.db. This keeps the server largely stateless apart from CA data and logs.
Common Operations
Building the Image
Build the Docker image:
docker build -t cryptic-server .
Build with a specific tag:
docker build -t cryptic-server:1.0.0 .
Starting the Server
Start in detached mode:
docker compose up -d
Start with logs visible:
docker compose up
Stopping the Server
Stop the container:
docker compose down
Stop and remove volumes (careful - this deletes data!):
docker compose down -v
Viewing Logs
Follow logs in real-time:
docker compose logs -f cryptic-server
View last 100 lines:
docker compose logs --tail=100 cryptic-server
Accessing the Container
Open a shell in the running container:
docker compose exec cryptic-server /bin/sh
Attach to the Erlang console:
docker compose exec cryptic-server bin/cryptic remote_console
Health Check
The container includes a health check that verifies the server is listening on port 8443:
# Check health status
docker inspect --format='{{.State.Health.Status}}' cryptic-server
# View health check logs
docker inspect --format='{{range .State.Health.Log}}{{.Output}}{{end}}' cryptic-server
Networking
Default Configuration
The docker-compose.yml creates a bridge network named cryptic-network:
networks:
cryptic-network:
driver: bridgeConnecting from Host
If you're running the client on the host machine, connect to localhost:8443:
cryptic_console ...
# In the Cryptic shell, connect to localhost:8443
Connecting from Another Container
Add your client container to the same network:
services:
my-client:
networks:
- cryptic-networkThen connect to cryptic-server:8443 (use the service name as hostname).
Troubleshooting
Container Won't Start
Check logs:
docker compose logs cryptic-server
Common issues:
- Certificate files not found: Ensure certificate paths in docker-compose.yml are correct
- Port already in use: Change the host port mapping in docker-compose.yml
- Permission denied: Ensure certificate files are readable
- NIF loading errors: The cryptic_nif.so library requires libsodium - this is included in the image
Connection Refused
Verify server is listening:
docker compose exec cryptic-server netstat -tlnp | grep 8443
Check firewall rules:
# On the host
sudo iptables -L | grep 8443
Verify CRYPTIC_SERVER_HOST:
docker compose exec cryptic-server env | grep CRYPTIC_SERVER_HOST
# Should show: CRYPTIC_SERVER_HOST=0.0.0.0
mTLS Handshake Failures
Verify certificates:
# Check server certificate
openssl x509 -in priv/ssl/server.crt -text -noout
# Verify certificate chain
openssl verify -CAfile priv/ssl/ca.crt priv/ssl/server.crt
Check client certificate:
# Ensure client has valid certificate signed by same CA
openssl verify -CAfile priv/ssl/ca.crt ~/.cryptic/<username>/<server>_<port>/certificates/<username>.crt
Performance Issues
Monitor container resources:
docker stats cryptic-server
Increase container limits in docker-compose.yml:
services:
cryptic-server:
deploy:
resources:
limits:
cpus: '2.0'
memory: 1G
reservations:
cpus: '1.0'
memory: 512MProduction Deployment
Security Considerations
- Use proper certificates: Don't use self-signed certificates in production
- Secure private keys: Set strict permissions (0400) on key files
- Run as non-root: The container runs as user
cryptic(UID 1000) - Network isolation: Use Docker networks to isolate the server
- Regular updates: Keep the base image and dependencies updated
Recommended Setup
- Use Docker secrets for sensitive data:
services: cryptic-server: secrets: - server_key - server_cert - ca_cert secrets: server_key: file: ./priv/ssl/server.key server_cert: file: ./priv/ssl/server.crt ca_cert: file: ./priv/ssl/ca.crt
3. Use health checks with orchestration (Kubernetes, Swarm):
healthcheck:
test: ["CMD", "nc", "-z", "localhost", "8443"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s- Set resource limits:
deploy: resources: limits: cpus: '2' memory: 2G
Advanced Usage
Building for Different Architectures
Build for ARM64 (e.g., Apple Silicon):
docker buildx build --platform linux/arm64 -t cryptic-server:arm64 .
Build multi-architecture image:
docker buildx build --platform linux/amd64,linux/arm64 -t cryptic-server:latest .
Custom Build Args
The Dockerfile is configured for Erlang 28.1 and Alpine Linux. The build process includes:
- Erlang/OTP 28.1 for the build stage
- Alpine Linux (latest) for the runtime stage
- libsodium for cryptographic operations
- Explicit ARM64 architecture support for Apple Silicon
Build with specific settings:
docker build -t cryptic-server .
Running Without Docker Compose
Run the container directly:
docker run -d \
--name cryptic-server \
-p 8443:8443 \
-v $(pwd)/priv/ssl/server.crt:/opt/cryptic/certs/server.crt:ro \
-v $(pwd)/priv/ssl/server.key:/opt/cryptic/certs/server.key:ro \
-v $(pwd)/priv/ssl/ca.crt:/opt/cryptic/certs/ca.crt:ro \
-v cryptic-logs:/opt/cryptic/logs \
-v cryptic-data:/opt/cryptic/data \
-e CRYPTIC_SERVER_HOST=0.0.0.0 \
-e CRYPTIC_SERVER_PORT=8443 \
-e CRYPTIC_CA_DB_FILE=/opt/cryptic/data/ca/cryptic_ca.db \
-e CRYPTIC_DEBUG=true \
--restart unless-stopped \
cryptic-server
Integration with CI/CD
Example GitHub Actions workflow:
name: Build Docker Image
on:
push:
branches: [ main ]
tags: [ 'v*' ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: false
tags: cryptic-server:latest
cache-from: type=gha
cache-to: type=gha,mode=maxReferences
Support
For issues or questions:
- Check the main README
- Review the logs
- Open an issue on GitHub