> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/stormkit-io/stormkit-io/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom Images

> Build and deploy custom Docker images for your self-hosted Stormkit instance with specific runtimes, dependencies, and configurations

Self-hosted Stormkit instances provide the flexibility to customize Docker images according to your specific deployment needs. You can build custom images that include additional runtimes, dependencies, tools, or configurations that your applications require.

## Understanding Stormkit's Runtime Management

Stormkit uses **mise** (formerly `rtx`) to dynamically install and manage runtimes during application deployment. This approach provides several benefits:

* **Flexible runtime versions** - Each project can specify its own runtime version
* **Automatic dependency management** - Runtimes are installed on-demand
* **Persistent storage** - Dependencies are cached in the home folder for reuse

<Note>
  For most use cases, Stormkit's dynamic runtime installation via mise is sufficient. Custom images are primarily needed for system-level dependencies that require root access.
</Note>

### Runtime vs Build-time Dependencies

Understanding the distinction between different types of dependencies is crucial:

**Runtime Dependencies (Managed by mise):**

* Programming language runtimes (Node.js, Python, Go, Rust, etc.)
* Language-specific package managers (`npm`, `pip`, `cargo`, etc.)
* Dependencies installed via mise configuration files
* Tools available through mise plugins

**Build-time Dependencies (Require Custom Images):**

* System packages requiring root access
* Custom build tools and utilities
* Base system configurations
* Native libraries and binaries

## Official Stormkit Images

Before creating custom images, understand the official Stormkit images available:

### ghcr.io/stormkit-io/hosting:latest

* **Purpose:** Serves the Stormkit API and deployed applications
* **Responsibilities:**
  * Web interface and API endpoints
  * Application hosting and serving
  * TLS certificate management
  * HTTP/HTTPS request handling
* **Optimization:** Optimized for production web serving

### ghcr.io/stormkit-io/workerserver:latest

* **Purpose:** Runs background jobs and deployments
* **Responsibilities:**
  * Application builds and deployments
  * Build queue processing
  * Background job execution
  * Deployment pipeline management
* **Optimization:** Optimized for build and deployment tasks

<Note>
  Most custom dependencies should be added to the **workerserver** image, as this is where builds and deployments execute.
</Note>

## When to Customize Images

Custom images are primarily needed for:

### System-Level Dependencies

Packages that require root access to install:

```dockerfile theme={null}
# Example: Image processing libraries
RUN apt-get update && apt-get install -y \
    imagemagick \
    graphicsmagick \
    libvips-dev
```

### Custom Build Tools

Tools not available through standard package managers:

```dockerfile theme={null}
# Example: Custom deployment tool
RUN wget -O /usr/local/bin/custom-tool https://example.com/custom-tool \
    && chmod +x /usr/local/bin/custom-tool
```

### Security Configurations

System-level security hardening and configurations:

```dockerfile theme={null}
# Example: Security configurations
RUN echo 'security.setting=value' >> /etc/app.conf
```

### Pre-installed System Utilities

Libraries that multiple projects need:

```dockerfile theme={null}
# Example: Media processing tools
RUN apt-get update && apt-get install -y \
    ffmpeg \
    ghostscript
```

<Warning>
  For runtime dependencies like Node.js versions or npm packages, use mise configuration instead of custom images. This provides more flexibility and easier updates.
</Warning>

## Persisting Dependencies

Stormkit stores all runtime dependencies in the home folder. To persist these dependencies across upgrades and restarts, mount the home directory:

```yaml docker-compose.yaml theme={null}
services:
  hosting:
    image: ghcr.io/stormkit-io/hosting:latest
    volumes:
      - hosting_home:/home/stormkit
    # Additional configuration...

  workerserver:
    image: ghcr.io/stormkit-io/workerserver:latest
    volumes:
      - workerserver_home:/home/stormkit
    # Additional configuration...

volumes:
  hosting_home:
  workerserver_home:
```

<Note>
  Persisting the home directory prevents re-downloading runtimes after container restarts and improves deployment performance.
</Note>

## Creating Custom Images

### Basic Custom Image Example

Create a `Dockerfile` extending the official workerserver image:

```dockerfile Dockerfile theme={null}
FROM ghcr.io/stormkit-io/workerserver:latest

# Switch to root to install system packages
USER root

# Install system packages that require root access
RUN apt-get update && apt-get install -y \
    imagemagick \
    graphicsmagick \
    libvips-dev \
    ffmpeg \
    && rm -rf /var/lib/apt/lists/*

# Install custom system tools
RUN wget -O /usr/local/bin/custom-tool https://example.com/custom-tool \
    && chmod +x /usr/local/bin/custom-tool

# Switch back to stormkit user (IMPORTANT!)
USER stormkit

# CMD is inherited from base image, no need to specify
```

<Warning>
  **Security Critical:** Always switch back to the `stormkit` user after installing packages. Running as root in production is a security risk.
</Warning>

### Advanced Example: Multiple Dependencies

```dockerfile Dockerfile.workerserver theme={null}
FROM ghcr.io/stormkit-io/workerserver:latest

USER root

# Install image processing libraries
RUN apt-get update && apt-get install -y \
    # Image processing
    imagemagick \
    graphicsmagick \
    libvips-dev \
    libvips-tools \
    # Video processing
    ffmpeg \
    # PDF processing
    ghostscript \
    poppler-utils \
    # Build tools
    build-essential \
    cmake \
    && rm -rf /var/lib/apt/lists/*

# Install additional Python packages system-wide (if needed)
RUN apt-get update && apt-get install -y \
    python3-dev \
    python3-pip \
    && rm -rf /var/lib/apt/lists/*

# Clean up
RUN apt-get clean && \
    rm -rf /var/cache/apt/* /tmp/* /var/tmp/*

USER stormkit
```

### Custom Hosting Image Example

If you need to customize the hosting image:

```dockerfile Dockerfile.hosting theme={null}
FROM ghcr.io/stormkit-io/hosting:latest

USER root

# Install SSL/TLS tools or monitoring agents
RUN apt-get update && apt-get install -y \
    certbot \
    monitoring-agent \
    && rm -rf /var/lib/apt/lists/*

USER stormkit
```

## Building Custom Images

<Steps>
  <Step title="Create Dockerfile">
    Create your `Dockerfile` in your project directory.
  </Step>

  <Step title="Build Image Locally">
    ```bash theme={null}
    docker build -t my-custom-stormkit-workerserver:latest -f Dockerfile .
    ```
  </Step>

  <Step title="Test Image (Optional)">
    ```bash theme={null}
    docker run --rm -it my-custom-stormkit-workerserver:latest /bin/bash
    ```
  </Step>

  <Step title="Tag for Registry (Optional)">
    ```bash theme={null}
    docker tag my-custom-stormkit-workerserver:latest \
      your-registry.com/stormkit-workerserver:latest
    ```
  </Step>

  <Step title="Push to Registry (Optional)">
    ```bash theme={null}
    docker push your-registry.com/stormkit-workerserver:latest
    ```
  </Step>
</Steps>

<Note>
  Pushing to a registry is optional if you're building directly on your deployment server.
</Note>

## Configuring Docker Compose

Update your `docker-compose.yaml` to use custom images:

### Option 1: Build from Local Dockerfile

```yaml docker-compose.yaml theme={null}
services:
  workerserver:
    # Comment out the image line
    # image: ghcr.io/stormkit-io/workerserver:latest
    
    # Add build configuration
    build:
      context: .
      dockerfile: Dockerfile.workerserver
    
    container_name: workerserver
    restart: always
    env_file:
      - .env
    volumes:
      - workerserver_home:/home/stormkit
    depends_on:
      - db
      - redis

  hosting:
    # Keep using official image or customize
    image: ghcr.io/stormkit-io/hosting:latest
    # OR build custom hosting image:
    # build:
    #   context: .
    #   dockerfile: Dockerfile.hosting
    
    container_name: hosting
    restart: always
    ports:
      - "80:80"
      - "443:443"
    env_file:
      - .env
    volumes:
      - hosting_home:/home/stormkit
    depends_on:
      - db
      - redis
      - workerserver

volumes:
  hosting_home:
  workerserver_home:
  postgres_data:
  redis_data:
```

### Option 2: Use Pre-built Image from Registry

```yaml docker-compose.yaml theme={null}
services:
  workerserver:
    image: your-registry.com/stormkit-workerserver:latest
    container_name: workerserver
    # ... rest of configuration
```

## Upgrading Custom Images

<Steps>
  <Step title="Pull Latest Base Image">
    ```bash theme={null}
    docker pull ghcr.io/stormkit-io/workerserver:latest
    ```
  </Step>

  <Step title="Rebuild Custom Image">
    ```bash theme={null}
    docker compose build --no-cache workerserver
    ```
  </Step>

  <Step title="Stop Services">
    ```bash theme={null}
    docker compose down workerserver hosting
    ```
  </Step>

  <Step title="Start with New Images">
    ```bash theme={null}
    docker compose up -d --build workerserver hosting
    ```
  </Step>

  <Step title="Verify Services">
    ```bash theme={null}
    docker compose ps
    docker compose logs -f workerserver hosting
    ```
  </Step>
</Steps>

<Note>
  The `--build` flag ensures Docker Compose rebuilds the image if there are changes.
</Note>

## Security Considerations

<Warning>
  Follow these security best practices when creating custom images:
</Warning>

### Use Official Base Images

```dockerfile theme={null}
# ✅ Good: Official Stormkit image
FROM ghcr.io/stormkit-io/workerserver:latest

# ❌ Bad: Unknown third-party image
FROM randomuser/stormkit:latest
```

### Minimize Attack Surface

```dockerfile theme={null}
# ✅ Good: Install only what's needed
RUN apt-get update && apt-get install -y \
    imagemagick \
    && rm -rf /var/lib/apt/lists/*

# ❌ Bad: Installing unnecessary packages
RUN apt-get update && apt-get install -y \
    imagemagick \
    build-essential \
    git \
    curl \
    wget \
    vim \
    nano
```

### Always Switch Back to Non-Root User

```dockerfile theme={null}
# ✅ Good: Switch back to stormkit user
USER root
RUN apt-get update && apt-get install -y imagemagick
USER stormkit

# ❌ Bad: Leaving as root user
USER root
RUN apt-get update && apt-get install -y imagemagick
# Missing: USER stormkit
```

### Keep Images Updated

```bash theme={null}
# Regularly update base images
docker pull ghcr.io/stormkit-io/workerserver:latest
docker compose build --no-cache
```

### Scan for Vulnerabilities

```bash theme={null}
# Scan images for security vulnerabilities
docker scan my-custom-stormkit-workerserver:latest

# Or use trivy
trivy image my-custom-stormkit-workerserver:latest
```

## Best Practices

### Layer Optimization

Combine RUN commands to reduce image layers:

```dockerfile theme={null}
# ✅ Good: Combined commands
RUN apt-get update && apt-get install -y \
    imagemagick \
    ffmpeg \
    && rm -rf /var/lib/apt/lists/*

# ❌ Bad: Separate commands create more layers
RUN apt-get update
RUN apt-get install -y imagemagick
RUN apt-get install -y ffmpeg
RUN rm -rf /var/lib/apt/lists/*
```

### Clean Up Cache

Remove package manager cache to reduce image size:

```dockerfile theme={null}
RUN apt-get update && apt-get install -y \
    imagemagick \
    && rm -rf /var/lib/apt/lists/* \
    && apt-get clean
```

### Use .dockerignore

Create a `.dockerignore` file to exclude unnecessary files:

```dockerignore .dockerignore theme={null}
node_modules
.git
.env
*.log
.DS_Store
```

### Document Customizations

Add comments explaining why packages are needed:

```dockerfile theme={null}
# Install image processing libraries for user avatar generation
RUN apt-get update && apt-get install -y \
    imagemagick \
    graphicsmagick \
    && rm -rf /var/lib/apt/lists/*
```

## Troubleshooting

### Build Fails: Permission Denied

**Cause:** Trying to install packages without switching to root.

**Solution:**

```dockerfile theme={null}
USER root
RUN apt-get update && apt-get install -y package-name
USER stormkit
```

### Container Crashes After Custom Image

**Cause:** Not switching back to `stormkit` user.

**Solution:** Ensure `USER stormkit` is at the end of your Dockerfile.

### Builds Are Slow

**Cause:** Not using Docker layer caching.

**Solution:**

* Order Dockerfile commands from least to most frequently changing
* Use BuildKit: `DOCKER_BUILDKIT=1 docker build ...`

### Large Image Size

**Cause:** Not cleaning up package manager cache.

**Solution:**

```dockerfile theme={null}
RUN apt-get update && apt-get install -y package \
    && rm -rf /var/lib/apt/lists/* \
    && apt-get clean
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Runtimes" icon="code" href="/self-hosting/runtimes">
    Manage programming language runtimes with mise
  </Card>

  <Card title="Troubleshooting" icon="wrench" href="/self-hosting/troubleshooting">
    Common issues and solutions
  </Card>
</CardGroup>
