# > Memos development environment < # # Available profiles: sqlite, mysql, postgres. # Use `docker compose --profile PROFILE_NAME up` to launch only services within the profile. # # Services in the `tools` profile are used for running one-off tasks like linting, generating code, etc. # # Services started in all database profiles: # Front-end: http://localhost:3001 # API: http://localhost:8081 # Adminer: http://localhost:8091 # # On Windows, run this before using docker-compose on a new terminal: # $Env:HOME=$Env:USERPROFILE # # > Start Memos in development mode: # docker compose -f ./scripts/docker-compose.dev.yaml --profile [sqlite|mysql|postgres] up --detach # # > Stop all services: # docker compose -f ./scripts/docker-compose.dev.yaml --profile sqlite --profile postgres --profile mysql down # # > Remove related volumes: (all other files are mapped to ./air/docker/ directory) # docker volume rm memos-dev_pnpm-store memos-dev_node-modules # # One-off tasks: # > pnpm: # docker compose -f ./scripts/docker-compose.dev.yaml run --rm pnpm [add|remove|update] [PACKAGE_NAME] [--save-dev] # # > buf: (run this after modifying .proto files) # docker compose -f ./scripts/docker-compose.dev.yaml run --rm buf generate # # > go: # docker compose -f ./scripts/docker-compose.dev.yaml run --rm go mod tidy -go=1.22 # # > golangci-lint: (run this before submitting Pull Requests affecting Go code) # docker compose -f ./scripts/docker-compose.dev.yaml run --rm golangci-lint run # # > goimports: (run this if golangci-lint shows "File is not `goimports`-ed" # docker compose -f ./scripts/docker-compose.dev.yaml run --rm goimports -local https://github.com/usememos/memos -w [FILE|.] # version: "3.0" name: memos-dev volumes: # pnpm uses hard links and node_modules uses symlinks. # Using volumes make things work properly on any host OS. node-modules: pnpm-store: services: web: profiles: ["sqlite", "mysql", "postgres"] image: node:20-alpine ports: [3001:3001] environment: DEV_PROXY_SERVER: http://api:8081/ NPM_CONFIG_UPDATE_NOTIFIER: false working_dir: &web-working-dir /work/web entrypoint: ["/bin/sh", "-c"] command: ["corepack enable && pnpm i --frozen-lockfile && pnpm dev"] tmpfs: &web-tmpfs /work/node_modules/:exec # avoid ERR_PNPM_LINKING_FAILED volumes: &web-volumes - node-modules:/work/web/node_modules - pnpm-store:/work/web/.pnpm-store - ../proto:/work/proto - ../web:/work/web - ../web/node_modules:/work/web/node_modules healthcheck: test: ["CMD", "wget", "-qO", "-", "http://localhost:3001"] interval: 10s timeout: 5s api: profiles: ["sqlite"] image: &api-image golang:1.22-alpine ports: &api-ports [8081:8081] environment: MEMOS_DRIVER: sqlite MEMOS_DATA: /var/opt/memos working_dir: &api-working-dir /work volumes: &api-volumes - $HOME/go/pkg/:/go/pkg/ # Share go mod cache with host - ../.air/docker/go-build:/root/.cache/go-build - ../.air/docker/go/bin:/go/bin - ../.air/docker/memosdata:/var/opt/memos - ..:/work/ configs: &api-configs - source: air-entrypoint.sh target: /usr/local/bin/entrypoint.sh entrypoint: &api-entrypoint ["/bin/sh", "/usr/local/bin/entrypoint.sh"] command: &api-command ["-c", "./scripts/.air.toml"] healthcheck: &api-healthcheck test: ["CMD", "wget", "-qO", "-", "http://localhost:8081/api/v1/ping"] interval: 10s timeout: 5s api-mysql: profiles: ["mysql"] depends_on: { mysql: { condition: service_healthy } } hostname: api environment: { MEMOS_DRIVER: mysql, MEMOS_DSN: memos:memos@tcp(mysql)/memos } image: *api-image ports: *api-ports working_dir: *api-working-dir volumes: *api-volumes configs: *api-configs entrypoint: *api-entrypoint command: *api-command healthcheck: *api-healthcheck api-postgres: profiles: ["postgres"] depends_on: { postgres: { condition: service_healthy } } hostname: api environment: MEMOS_DSN: "postgresql://memos:memos@postgres:5432/memos?sslmode=disable" MEMOS_DRIVER: postgres image: *api-image ports: *api-ports working_dir: *api-working-dir volumes: *api-volumes configs: *api-configs entrypoint: *api-entrypoint command: *api-command healthcheck: *api-healthcheck mysql: profiles: ["mysql"] image: mysql environment: MYSQL_USER: memos MYSQL_PASSWORD: memos MYSQL_DATABASE: memos MYSQL_ALLOW_EMPTY_PASSWORD: "yes" volumes: [../.air/docker/mysql:/var/lib/mysql] healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s timeout: 5s postgres: profiles: ["postgres"] image: postgres:alpine hostname: postgres volumes: [../.air/docker/postgres:/var/lib/postgresql/data] environment: { POSTGRES_DB: memos, POSTGRES_USER: memos, POSTGRES_PASSWORD: memos } healthcheck: test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"] interval: 10s timeout: 5s pnpm: profiles: ["tools"] image: node:20-alpine environment: { NPM_CONFIG_UPDATE_NOTIFIER: false } working_dir: *web-working-dir volumes: *web-volumes tmpfs: *web-tmpfs configs: - source: pnpm-entrypoint.sh target: /usr/local/bin/entrypoint.sh entrypoint: ["sh", "/usr/local/bin/entrypoint.sh"] buf: profiles: ["tools"] image: bufbuild/buf working_dir: /work/proto command: generate volumes: - ../proto:/work/proto - ../web/src/types/:/work/web/src/types/ go: profiles: ["tools"] image: *api-image working_dir: *api-working-dir volumes: *api-volumes entrypoint: ["go"] goimports: profiles: ["tools"] image: *api-image working_dir: *api-working-dir volumes: *api-volumes configs: - source: goimports-entrypoint.sh target: /usr/local/bin/entrypoint.sh entrypoint: ["/bin/sh", "/usr/local/bin/entrypoint.sh"] golangci-lint: profiles: ["tools"] image: *api-image working_dir: *api-working-dir volumes: *api-volumes configs: - source: golangci-lint-entrypoint.sh target: /usr/local/bin/entrypoint.sh entrypoint: ["/bin/sh", "/usr/local/bin/entrypoint.sh"] adminer-mysql: profiles: ["mysql"] depends_on: { mysql: { condition: service_healthy } } image: adminer environment: &adminer-environment ADMINER_DEFAULT_DRIVER: server # "server" is mysql ADMINER_DEFAULT_SERVER: mysql ADMINER_DEFAULT_USERNAME: memos ADMINER_DEFAULT_PASSWORD: memos ADMINER_DEFAULT_DB: memos ADMINER_DESIGN: dracula # light: pepa-linha | https://www.adminer.org/#extras ADMINER_PLUGINS: tables-filter table-structure edit-textarea dump-json # https://www.adminer.org/en/plugins/ ports: &adminer-ports [127.0.0.1:8091:8080] healthcheck: &adminer-healthcheck test: 'php -r "exit(strpos(file_get_contents(\"http://localhost:8080/\"), \"Adminer\") !== false ? 0 : 1);"' interval: 10s timeout: 5s configs: &adminer-configs - source: adminer-index.php target: /var/www/html/index.php adminer-postgres: profiles: ["postgres"] depends_on: { postgres: { condition: service_healthy } } image: adminer ports: *adminer-ports healthcheck: *adminer-healthcheck configs: *adminer-configs environment: <<: *adminer-environment ADMINER_DEFAULT_DRIVER: pgsql ADMINER_DEFAULT_SERVER: postgres adminer-sqlite: profiles: ["sqlite"] image: adminer ports: *adminer-ports healthcheck: *adminer-healthcheck configs: *adminer-configs environment: <<: *adminer-environment ADMINER_DEFAULT_PASSWORD: "" ADMINER_DEFAULT_DRIVER: sqlite ADMINER_DEFAULT_DB: /data/memos_dev.db volumes: [../.air/docker/memosdata:/data] configs: # Patched version of adminer index.php to fill the login form with default values # and allow passwordless login whenever ADMINER_DEFAULT_DRIVER is sqlite. adminer-index.php: content: |