Add Dockerfile, docker-compose.yml, .dockerignore

Multi-stage Dockerfile:
- Stage 1: install deps, build TS, build web UI
- Stage 2: minimal production image with sqlite, only prod deps
- Exposes port 4738, uses /data volume for SQLite

docker-compose.yml for easy local deployment with .env support.
.dockerignore to keep the build context lean.
README updated with Docker usage.
.env.template adds DATA_DIR.
This commit is contained in:
Agent 2026-08-18 12:40:42 -04:00
parent f220301154
commit 31deaf3aa5
5 changed files with 134 additions and 0 deletions

15
.dockerignore Normal file
View file

@ -0,0 +1,15 @@
node_modules/
dist/
web/dist/
.git/
.gitattributes
.gitignore
*.md
test/
mailship.db
test-mailship.db
test-data/
.env
.env.local
*.p8
*firebase*

View file

@ -8,3 +8,4 @@ SEARCH_RELAYS=relay.nostr.band
POSTMARK_API_KEY=
POSTMARK_SENDER_ADDRESS=
PORT=4738
DATA_DIR=./data

67
Dockerfile Normal file
View file

@ -0,0 +1,67 @@
# syntax=docker/dockerfile:1
FROM node:22-alpine AS build
WORKDIR /app
# Install build dependencies for native modules
RUN apk add --no-cache python3 make g++ git
# Install pnpm
RUN corepack enable && corepack prepare pnpm@latest --activate
# Copy package files
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY remove-pnpm-overrides.js ./
# Remove pnpm link overrides for production build
RUN node remove-pnpm-overrides.js package.json
# Install dependencies
RUN pnpm install --frozen-lockfile
# Copy source
COPY tsconfig.json ./
COPY src/ ./src/
COPY src/pages/ ./src/pages/
COPY src/emails/ ./src/emails/
# Build TypeScript
RUN pnpm run build
# Build web UI
COPY web/package.json web/pnpm-lock.yaml web/tsconfig.json web/vite.config.js web/index.html ./
COPY web/src/ ./web/src/
RUN cd web && pnpm install --frozen-lockfile && pnpm run build
# Production image
FROM node:22-alpine AS production
WORKDIR /app
RUN apk add --no-cache sqlite
# Install pnpm
RUN corepack enable && corepack prepare pnpm@latest --activate
# Copy production dependencies
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY remove-pnpm-overrides.js ./
RUN node remove-pnpm-overrides.js package.json
RUN pnpm install --frozen-lockfile --prod
# Copy build artifacts
COPY --from=build /app/dist/ ./dist/
COPY --from=build /app/web/dist/ ./web/dist/
COPY --from=build /app/src/pages/ ./dist/pages/
COPY --from=build /app/src/emails/ ./dist/emails/
# Create data directory for SQLite
RUN mkdir -p /data
EXPOSE 4738
ENV NODE_ENV=production
ENV PORT=4738
ENV DATA_DIR=/data
CMD ["node", "dist/index.js"]

View file

@ -79,6 +79,34 @@ pnpm run build
pnpm run start
```
## Docker
### Quick start
```sh
cp .env.template .env
# Fill in your secrets in .env
docker compose up -d
```
### Build and run manually
```sh
docker build -t mailship .
docker run -d \
-p 4738:4738 \
-v mailship-data:/data \
--env-file .env \
mailship
```
## Tests
```sh
pnpm test # Run integration tests
pnpm test:server # Start server for manual testing
```
## Forked from Anchor
Mailship is a fork of [Anchor](https://github.com/coracle-social/anchor), stripped of push notification support and adapted for NIP-9a relay push event intake.

23
docker-compose.yml Normal file
View file

@ -0,0 +1,23 @@
services:
mailship:
build: .
ports:
- "4738:4738"
volumes:
- mailship-data:/data
environment:
- MAILSHIP_SECRET=${MAILSHIP_SECRET:?required}
- MAILSHIP_NAME=${MAILSHIP_NAME:-Mailship}
- MAILSHIP_URL=${MAILSHIP_URL:?required}
- BASE_URL=${BASE_URL:?required}
- POSTMARK_API_KEY=${POSTMARK_API_KEY:?required}
- POSTMARK_SENDER_ADDRESS=${POSTMARK_SENDER_ADDRESS:?required}
- DEFAULT_RELAYS=${DEFAULT_RELAYS:-wss://relay.damus.io,wss://relay.primal.net}
- INDEXER_RELAYS=${INDEXER_RELAYS:-wss://purplepag.es,wss://relay.damus.io}
- SEARCH_RELAYS=${SEARCH_RELAYS:-wss://relay.nostr.band}
- PORT=4738
- DATA_DIR=/data
restart: unless-stopped
volumes:
mailship-data: