OCI Builder
    Nixpacks Successor

    Deploy Railpack as a Build Server

    Zero-config container builds for Node, Python, Go, Ruby, PHP, Java, Rust, Elixir, Deno and more — no hand-written Dockerfiles.

    At a Glance

    ProjectRailpack (Railway)
    LicenseMIT
    Recommended PlanRamNode Cloud VPS 2 vCPU / 4 GB / 50 GB+
    OSUbuntu 22.04 / 24.04
    EngineBuildKit (LLB)
    1

    System Prep

    Update + basics
    sudo apt update && sudo apt upgrade -y
    sudo apt install -y curl ca-certificates gnupg lsb-release git jq
    2

    Install Docker

    Official Docker repo
    sudo install -m 0755 -d /etc/apt/keyrings
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
    sudo chmod a+r /etc/apt/keyrings/docker.gpg
    
    echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    
    sudo apt update
    sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
    sudo systemctl enable --now docker
    sudo usermod -aG docker $USER  # log out/in for group change
    3

    Run BuildKit

    Persistent BuildKit container
    docker run -d \
      --name buildkit \
      --restart unless-stopped \
      --privileged \
      -v /var/lib/buildkit:/var/lib/buildkit \
      moby/buildkit:latest
    
    echo "export BUILDKIT_HOST='docker-container://buildkit'" | sudo tee /etc/profile.d/buildkit.sh
    sudo chmod +x /etc/profile.d/buildkit.sh
    source /etc/profile.d/buildkit.sh
    
    docker ps --filter name=buildkit
    docker logs buildkit 2>&1 | tail -5
    4

    Install Railpack CLI

    Direct binary install
    RAILPACK_VERSION=$(curl -s https://api.github.com/repos/railwayapp/railpack/releases/latest | jq -r .tag_name)
    ARCH=$(uname -m | sed 's/x86_64/amd64/' | sed 's/aarch64/arm64/')
    cd /tmp
    curl -L "https://github.com/railwayapp/railpack/releases/download/${RAILPACK_VERSION}/railpack-${RAILPACK_VERSION}-linux-${ARCH}.tar.gz" -o railpack.tar.gz
    tar -xzf railpack.tar.gz
    sudo install -m 0755 railpack /usr/local/bin/railpack
    railpack --version
    5

    Build Your First Image

    Build a sample Vite + React app
    mkdir -p ~/builds && cd ~/builds
    npm create vite@latest hello-vite -- --template react
    cd hello-vite
    npm install
    railpack build .
    
    docker images | grep hello-vite
    docker run --rm -p 3000:3000 hello-vite
    Optional railpack.json overrides
    {
      "providers": ["node"],
      "packages": { "node": "20.18.0", "pnpm": "9.x" },
      "aptPackages": ["imagemagick", "ffmpeg"],
      "steps": {
        "build": {
          "commands": ["pnpm install --frozen-lockfile", "pnpm build"]
        }
      },
      "deploy": {
        "startCommand": "pnpm start",
        "variables": { "NODE_ENV": "production" }
      }
    }
    6

    Push to a Container Registry

    GitHub Container Registry
    echo $GITHUB_TOKEN | docker login ghcr.io -u your-username --password-stdin
    docker tag your-app:v1.2.3 ghcr.io/your-org/your-app:v1.2.3
    docker push ghcr.io/your-org/your-app:v1.2.3
    Reusable build wrapper /usr/local/bin/rp-build
    #!/usr/bin/env bash
    set -euo pipefail
    
    GIT_URL="$1"; IMAGE_NAME="$2"; IMAGE_TAG="$3"; REGISTRY="${4:-}"
    
    BUILD_DIR=$(mktemp -d)
    trap 'rm -rf "$BUILD_DIR"' EXIT
    
    git clone --depth 1 --branch "${IMAGE_TAG}" "${GIT_URL}" "${BUILD_DIR}" 2>/dev/null || \
        git clone --depth 1 "${GIT_URL}" "${BUILD_DIR}"
    
    cd "${BUILD_DIR}"
    railpack build . --name "${IMAGE_NAME}" --tag "${IMAGE_TAG}"
    
    if [[ -n "${REGISTRY}" ]]; then
        FULL="${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}"
        docker tag "${IMAGE_NAME}:${IMAGE_TAG}" "${FULL}"
        docker push "${FULL}"
    fi
    7

    Git Hook Auto-Build

    Bare repo + post-receive hook
    sudo mkdir -p /srv/git/your-app.git
    sudo chown $USER:$USER /srv/git/your-app.git
    cd /srv/git/your-app.git
    git init --bare
    
    cat > hooks/post-receive <<'EOF'
    #!/usr/bin/env bash
    set -euo pipefail
    while read oldrev newrev refname; do
        branch=$(git rev-parse --symbolic --abbrev-ref "$refname")
        if [[ "$branch" == "main" ]]; then
            BUILD_DIR=$(mktemp -d)
            git --work-tree="$BUILD_DIR" --git-dir=/srv/git/your-app.git checkout -f main
            cd "$BUILD_DIR"
            railpack build . --name your-app --tag "$(git rev-parse --short HEAD)"
            rm -rf "$BUILD_DIR"
        fi
    done
    EOF
    chmod +x hooks/post-receive
    8

    Cache + Resource Limits

    Daily prune
    sudo tee /etc/cron.daily/buildkit-prune > /dev/null <<'EOF'
    #!/usr/bin/env bash
    docker exec buildkit buildctl prune --keep-duration 72h >/dev/null 2>&1 || true
    EOF
    sudo chmod +x /etc/cron.daily/buildkit-prune
    Cap BuildKit resources
    docker stop buildkit && docker rm buildkit
    docker run -d --name buildkit --restart unless-stopped --privileged \
      --memory 4g --cpus 2.0 \
      -v /var/lib/buildkit:/var/lib/buildkit \
      moby/buildkit:latest