This document describes the repository's directory layout, build tooling, and development workflows. It covers the frontend build system (pnpm + Vite), backend build system (Go), Docker containerization, and the CI/CD pipeline that produces multi-architecture images.
For deployment-specific configuration and running the application, see Docker and Container Deployment. For contributing and setting up a local development environment, see Setting Up Development Environment.
The repository follows a monorepo structure with clear separation between frontend, backend, and infrastructure concerns:
Sources: web/package.json server/server.go scripts/Dockerfile
| Directory | Purpose | Key Files |
|---|---|---|
web/ | Frontend application (React + TypeScript) | package.json, vite.config.ts, tsconfig.json |
server/ | Backend application (Go) | server.go, router/, service/ |
proto/ | Protocol buffer definitions shared between frontend/backend | api/, store/ |
store/ | Database abstraction layer and driver implementations | db.go, driver/ |
cmd/memos/ | Main executable entry point | main.go |
internal/ | Internal-only packages (profile, log, util) | profile/, log/, util/ |
plugin/ | Plugin system (webhooks, storage, etc.) | webhook/, storage/ |
scripts/ | Build and deployment scripts | Dockerfile, entrypoint.sh |
.github/workflows/ | CI/CD pipeline definitions | *.yml |
Sources: Repository structure from all provided files
The frontend uses pnpm as the package manager and Vite as the build tool. The build system is configured for both development and production builds.
The project requires pnpm version 10 and Node.js 22 as specified in .github/workflows/frontend-tests.yml16-17
Key configuration in web/package.json94-97:
This ensures that only esbuild is built from source during installation, which is necessary for platform compatibility.
The web/package.json4-10 defines the following npm scripts:
| Script | Command | Purpose |
|---|---|---|
dev | vite | Start development server with HMR |
build | vite build | Production build to web/dist/ |
release | vite build --mode release --outDir=../server/router/frontend/dist --emptyOutDir | Production build for embedding in Go binary |
lint | tsc --noEmit --skipLibCheck && biome check src | Type-check and lint |
lint:fix | biome check --write src | Auto-fix linting issues |
format | biome format --write src | Format code |
The release script is the critical build command used in CI/CD. It:
release mode../server/router/frontend/dist/ (outside the web/ directory)--emptyOutDir to clear the output directory firstSources: web/package.json4-10
Vite configuration includes:
@tailwindcss/vite pluginThe build output consists of:
index.html - Entry pointassets/ - JavaScript bundles, CSS, and static assetsSources: web/package.json87 web/package.json29
The frontend uses TypeScript 5.9.3 (web/package.json91) with:
skipLibCheck for faster compilationSources: web/package.json91 web/package.json8
Biome (web/package.json72) is used for both linting and formatting, replacing ESLint and Prettier:
The CI pipeline runs pnpm lint to enforce code quality (.github/workflows/frontend-tests.yml44-45).
Sources: web/package.json72 web/package.json9-10 .github/workflows/frontend-tests.yml44-45
The backend is built using standard Go 1.25.7 tooling with no external build systems required.
Dependencies are managed via Go modules:
go 1.25.7go mod download caches dependenciesgo mod tidy ensures consistencyThe CI pipeline verifies that go.mod is tidy (.github/workflows/backend-tests.yml35-38):
Sources: go.mod3 .github/workflows/backend-tests.yml35-38
| Dependency | Version | Purpose |
|---|---|---|
github.com/labstack/echo/v5 | v5.0.3 | HTTP server framework |
connectrpc.com/connect | v1.19.1 | gRPC implementation |
github.com/grpc-ecosystem/grpc-gateway/v2 | v2.27.2 | HTTP/JSON to gRPC gateway |
modernc.org/sqlite | v1.38.2 | Pure-Go SQLite driver |
github.com/go-sql-driver/mysql | v1.9.3 | MySQL driver |
github.com/lib/pq | v1.10.9 | PostgreSQL driver |
github.com/aws/aws-sdk-go-v2 | v1.39.2 | S3 storage integration |
golang.org/x/crypto | v0.47.0 | Password hashing |
google.golang.org/protobuf | v1.36.9 | Protocol buffer runtime |
Sources: go.mod5-38
The standard Go build command is used in scripts/Dockerfile21-26:
Build flags explained:
CGO_ENABLED=0 - Pure Go binary (no C dependencies)-trimpath - Remove file system paths for reproducible builds-ldflags="-s -w" - Strip debug symbols for smaller binary-ldflags="-extldflags '-static'" - Statically link C libraries-tags netgo,osusergo - Use pure Go implementations of network and user functions./cmd/memos - Entry point packageThe resulting binary is self-contained and portable across systems.
Sources: scripts/Dockerfile21-26
Tests are run with:
The CI pipeline splits tests into 4 groups for parallel execution (.github/workflows/backend-tests.yml52):
Sources: .github/workflows/backend-tests.yml64-84
golangci-lint v2.4.0 is used for static analysis (.github/workflows/backend-tests.yml40-44):
Sources: .github/workflows/backend-tests.yml40-44
The complete build process combines frontend and backend builds into a single executable binary with embedded assets.
Sources: web/package.json7 scripts/Dockerfile15-26
The frontend must be built before the backend build:
This produces optimized static files in server/router/frontend/dist/:
index.html - HTML entry pointassets/*.js - JavaScript bundles (code-split)assets/*.css - Stylesheet bundlesSources: web/package.json7 .github/workflows/build-canary-image.yml35-39
The server/router/frontend package uses Go's embed directive to include the frontend files in the binary at compile time. The exact implementation would look similar to:
This embeds all files from server/router/frontend/dist/ into the compiled Go binary.
Sources: server/server.go64 (references frontend service)
After the frontend is embedded, build the Go binary:
The resulting memos binary is fully self-contained and can be deployed without any external files.
Sources: scripts/Dockerfile21-26
The architecture enables single-binary deployment:
The Echo HTTP server serves both:
Sources: server/server.go64 server/server.go72-73
Frontend development:
This starts Vite dev server with hot module replacement at http://localhost:5173.
Backend development:
This starts the server at http://localhost:5230 (default port).
Sources: web/package.json5 scripts/Dockerfile52
air or reflex for automatic restart on code changes (not included in repository)When modifying .proto files:
This regenerates:
proto/gen/The CI pipeline validates proto files with .github/workflows/proto-linter.yml30-33:
Sources: .github/workflows/proto-linter.yml30-40
Before committing, run:
Frontend:
Backend:
Sources: web/package.json8 .github/workflows/backend-tests.yml35-38
The CI/CD system uses GitHub Actions with separate workflows for testing and image building.
Sources: .github/workflows/backend-tests.yml .github/workflows/frontend-tests.yml .github/workflows/build-canary-image.yml .github/workflows/build-stable-image.yml
Triggers: Push to main, PR affecting *.go, go.mod, go.sum
Jobs:
go.mod tidiness and runs golangci-lintstore - Database layer tests (SQLite, MySQL, PostgreSQL)server - HTTP server tests with race detectorplugin - Plugin system testsother - Remaining packagesCoverage reports are uploaded to Codecov for main branch builds.
Sources: .github/workflows/backend-tests.yml21-92
Triggers: Push to main, PR affecting web/**
Jobs:
Sources: .github/workflows/frontend-tests.yml20-73
Triggers: Push to main, PR affecting proto/**
Jobs:
buf lint and buf format -d to ensure proto files are properly formattedSources: .github/workflows/proto-linter.yml16-41
Trigger: Every push to main branch
Process:
pnpm releaselinux/amd64linux/arm64canaryneosmemo/memos:canary) and GHCR (ghcr.io/usememos/memos:canary)Sources: .github/workflows/build-canary-image.yml11-167
Trigger: Push to release/** branches or v*.*.* tags
Process:
pnpm releaselinux/amd64linux/arm64linux/arm/v7 (32-bit ARM)stablevX.Y.Z (full version)X.Y (minor version)Sources: .github/workflows/build-stable-image.yml10-185
The build pipeline uses Docker Buildx with QEMU for cross-platform builds:
Key implementation details:
Users automatically pull the correct image for their platform when running docker pull neosmemo/memos:stable.
Sources: .github/workflows/build-canary-image.yml56-111 .github/workflows/build-stable-image.yml68-125
The Docker build uses a multi-stage Dockerfile to optimize image size and build caching.
Optimizations:
--mount=type=cache - Caches Go modules and build cache across builds--platform=$BUILDPLATFORM - Builds on native platform (faster)GOOS/GOARCH for target platformSources: scripts/Dockerfile1-26
Key features:
/var/opt/memosSources: scripts/Dockerfile29-56
The scripts/entrypoint.sh1-46 handles:
*_FILE environment variables:
This enables secure secret management in Docker Swarm and Kubernetes.
Sources: scripts/entrypoint.sh1-46
The build context includes:
server/router/frontend/dist/ before Docker build.dockerignore excludes unnecessary files (not shown in provided files)Best practice: Build frontend locally or in CI before Docker build:
Sources: scripts/Dockerfile15
pnpm store caching in CI (.github/workflows/build-canary-image.yml25-34):
This caches downloaded npm packages, reducing build time from minutes to seconds.
Sources: .github/workflows/build-canary-image.yml25-34
Go module caching (scripts/Dockerfile9-10):
Go build cache (scripts/Dockerfile18-19):
These BuildKit cache mounts persist across builds, significantly speeding up compilation.
Sources: scripts/Dockerfile9-19
GitHub Actions uses Docker layer caching (.github/workflows/build-canary-image.yml95-96):
This caches Docker layers in GitHub Actions cache storage, reusing unchanged layers across builds.
Sources: .github/workflows/build-canary-image.yml95-96
Frontend artifacts are built once and shared across platform builds (.github/workflows/build-canary-image.yml41-46):
This eliminates redundant frontend builds (5-10 minutes saved per platform).
Sources: .github/workflows/build-canary-image.yml41-46 .github/workflows/build-canary-image.yml63-67
| Task | Command | Location |
|---|---|---|
| Install frontend deps | pnpm install --frozen-lockfile | web/ |
| Dev frontend | pnpm dev | web/ |
| Build frontend (dev) | pnpm build | web/ |
| Build frontend (prod) | pnpm release | web/ |
| Lint frontend | pnpm lint | web/ |
| Download Go deps | go mod download | Root |
| Build backend | go build -o memos ./cmd/memos | Root |
| Run tests | go test ./... | Root |
| Lint backend | golangci-lint run | Root |
| Generate protos | buf generate | proto/ |
| Build Docker image | docker build -f scripts/Dockerfile . | Root |
| Run Docker container | docker run -v /data:/var/opt/memos -p 5230:5230 neosmemo/memos | - |
Sources: web/package.json go.mod scripts/Dockerfile
Refresh this wiki