Skip to content

Docker

ElyOS fully supports Docker-based development and deployment. This document details how to use Docker with ElyOS.

Using Docker provides several benefits:

  • Consistent environment — every developer and server uses the same environment
  • Simple setup — no need to install Node.js, Bun, or PostgreSQL locally
  • Isolated services — database and application run in separate containers
  • Quick startup — entire system starts with a single command
  • Reproducible builds — Docker image always builds the same way

Install Docker on your system:

Installing Bun is not required but makes running Docker commands easier:

Terminál
curl -fsSL https://bun.sh/install | bash

Docker can be used without Bun, just run raw docker compose commands.

Terminál
git clone https://github.com/ElyOS-webOS/elyos-core
cd elyos-core

Copy the example file and fill in values:

Terminál
cp .env.example .env

With Varlock + Infisical (recommended):

.env
INFISICAL_CLIENT_ID=your-machine-identity-client-id
INFISICAL_CLIENT_SECRET=your-machine-identity-client-secret

Without Infisical (local fallback mode):

.env
VARLOCK_FALLBACK=local
# ... fill in all variables from .env.example

With Bun:

Terminál
bun docker:up

Without Bun:

Terminál
docker compose -f docker/docker-compose.yml up -d

Open in browser: http://localhost:3000

The docker/docker-compose.yml file defines three services that start in order:

PostgreSQL 18 database with custom image including postgres-json-schema extension.

postgres:
build:
context: postgres
dockerfile: Dockerfile
ports:
- '${POSTGRES_PORT:-5432}:5432'
volumes:
- elyos-data:/var/lib/postgresql
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U ${POSTGRES_USER:-elyos} -d ${POSTGRES_DB:-elyos}']
interval: 10s
timeout: 5s
retries: 5

Features:

  • Port: 5432 (configurable: POSTGRES_PORT)
  • Persistent storage: elyos-data volume
  • Health check: checks database availability every 10 seconds
  • Auto-restart: unless-stopped

One-time initialization: runs Drizzle migrations and loads seed data.

db-init:
command: >
sh -c 'bun --filter @elyos/database db:init ${RESET:+-- --reset}'
depends_on:
postgres:
condition: service_healthy
restart: 'no'

Features:

  • Only starts when postgres service is healthy
  • Runs bun db:init command
  • One-time execution: restart: no
  • Supports RESET=1 environment variable for complete database reset

ElyOS web application (SvelteKit + Express + Socket.IO).

elyos:
ports:
- '${ELYOS_PORT:-3000}:3000'
depends_on:
db-init:
condition: service_completed_successfully
volumes:
- ../apps/web/uploads:/app/uploads
restart: unless-stopped

Features:

  • Port: 3000 (configurable: ELYOS_PORT)
  • Only starts when db-init completes successfully
  • Persistent file storage: uploads folder
  • Auto-restart: unless-stopped
  • Health check: checks /api/health endpoint every 30 seconds

Services start in sequence, ensuring proper dependencies:

1. postgres (starts)
2. postgres (healthy)
3. db-init (starts and runs)
4. db-init (completed successfully)
5. elyos (starts)

The db-init container is idempotent — safe to run multiple times, doesn’t duplicate data (upsert logic).

Terminál
bun docker:up

If complete database reset is needed (delete all data and reseed):

Terminál
RESET=1 bun docker:up

This runs the same db-init container but truncates all tables before seeding.

Without Bun:

Terminál
RESET=1 docker compose -f docker/docker-compose.yml up -d

Docker-based development supports hot reload via volume mount. Modify docker-compose.yml:

elyos:
volumes:
- ../apps/web:/app/apps/web
- ../packages:/app/packages
command: bun run app:dev

If you want only the database in Docker and run the application locally:

Terminál
bun docker:db

This starts only the postgres service.

Without Bun:

Terminál
docker compose -f docker/docker-compose.yml up -d postgres
Terminál
# Start containers (background)
bun docker:up
# or
docker compose -f docker/docker-compose.yml up -d
# Stop containers
bun docker:down
# or
docker compose -f docker/docker-compose.yml down
# Follow container logs
bun docker:logs
# or
docker compose -f docker/docker-compose.yml logs -f
# Start PostgreSQL only
bun docker:db
# or
docker compose -f docker/docker-compose.yml up -d postgres
Terminál
# List running containers
docker ps
# List all containers (including stopped)
docker ps -a
# View container logs
docker logs elyos-app
docker logs elyos-postgres
docker logs elyos-db-init
Terminál
# Enter ElyOS container
docker exec -it elyos-app sh
# Enter PostgreSQL container
docker exec -it elyos-postgres psql -U elyos -d elyos
Terminál
# Remove containers and network
docker compose -f docker/docker-compose.yml down
# Remove containers, network, and volumes (database data too)
docker compose -f docker/docker-compose.yml down -v
# Remove images
docker rmi elyos-elyos elyos-postgres

The docker/Dockerfile uses an optimized multi-stage build with three phases:

FROM oven/bun:1 AS deps
WORKDIR /app
COPY package.json bun.lock ./
COPY apps/web/package.json ./apps/web/
RUN bun install --frozen-lockfile

Features:

  • Copies only package.json and bun.lock files
  • Cacheable layer — if dependencies don’t change, this layer comes from cache
  • Frozen lockfile — reproducible build
FROM oven/bun:1 AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY apps/web ./apps/web
COPY packages/database ./packages/database
ENV NODE_ENV=production
RUN bun run app:build

Features:

  • Copies dependencies from deps phase
  • Copies source code
  • Runs SvelteKit build (adapter-node output)
FROM oven/bun:1-alpine AS runner
WORKDIR /app
RUN addgroup -g 1001 -S nodejs && \
adduser -S elyos -u 1001 -G nodejs
COPY --from=builder --chown=elyos:nodejs /app/apps/web/build ./apps/web/build
RUN bun install --production --frozen-lockfile
RUN bun add -g varlock
USER elyos
CMD ["varlock", "run", "--", "bun", "run", "apps/web/server.js"]

Features:

  • Alpine Linux-based image — minimal size
  • Non-root user — security
  • Production dependencies only
  • Global Varlock installation — secrets management
  • Health check — checks /api/health endpoint
Terminál
docker build -f docker/Dockerfile -t elyos/core:latest .
Terminál
docker build \
-f docker/Dockerfile \
-t elyos/core:latest \
--build-arg NODE_ENV=production \
.
Terminál
docker buildx build \
-f docker/Dockerfile \
-t elyos/core:latest \
--platform linux/amd64,linux/arm64 \
.

Docker Compose automatically loads the root .env file. The following variables are configurable:

VariableDefaultDescription
ELYOS_PORT3000ElyOS application port
POSTGRES_PORT5432PostgreSQL port
POSTGRES_USERelyosPostgreSQL username
POSTGRES_PASSWORDelyos123PostgreSQL password
POSTGRES_DBelyosPostgreSQL database name
INFISICAL_CLIENT_ID-Infisical Machine Identity Client ID
INFISICAL_CLIENT_SECRET-Infisical Machine Identity Client Secret
VARLOCK_FALLBACK-Varlock fallback mode (local)
RESET-Database reset (1 = complete reset)

Problem: Container stops immediately after starting.

Solution:

  1. Check logs:

    Terminál
    docker logs elyos-app
  2. Check environment variables:

    Terminál
    docker exec elyos-app env
  3. Check health check:

    Terminál
    docker inspect elyos-app | grep -A 10 Health

Problem: ECONNREFUSED or Connection refused error.

Solution:

  1. Check if postgres container is running:

    Terminál
    docker ps | grep postgres
  2. Check DATABASE_URL environment variable:

    Terminál
    # Correct format:
    postgresql://elyos:elyos123@postgres:5432/elyos
  3. Wait for postgres container to be healthy:

    Terminál
    docker compose -f docker/docker-compose.yml ps

Problem: Bind for 0.0.0.0:3000 failed: port is already allocated.

Solution:

  1. Change port in .env file:

    Terminál
    ELYOS_PORT=3001
  2. Or stop the other service:

    Terminál
    lsof -ti:3000 | xargs kill -9