Building Container Images
Rise CLI supports multiple build backends for creating container images from your application code.
Build Backends
Docker (Dockerfile)
Multiple Docker-based backends are available for building from a Dockerfile:
# Standard docker/podman build (default)
rise build myapp:latest --backend docker
rise build myapp:latest --backend docker:build # alias for docker
# Docker buildx (with BuildKit features like secrets)
rise build myapp:latest --backend docker:buildx
# Plain buildctl (BuildKit directly, requires buildctl CLI)
rise build myapp:latest --backend buildctl
Backend comparison:
| Backend | Build Tool | SSL Secrets | Push During Build |
|---|---|---|---|
docker / docker:build | docker build | No | No (separate push) |
docker:buildx | docker buildx build | Yes | Yes (--push) |
buildctl | buildctl | Yes | Yes |
When to use each:
docker:build- Simple builds, maximum compatibilitydocker:buildx- Need BuildKit features (secrets, caching, multi-platform)buildctl- Direct BuildKit access, CI environments without Docker
Pack (Cloud Native Buildpacks)
Uses pack build with Cloud Native Buildpacks:
rise build myapp:latest --backend pack
rise build myapp:latest --backend pack --builder paketobuildpacks/builder-jammy-base
rise deployment create myproject --backend pack
Railpack (Railway Railpacks)
Uses Railway’s Railpacks with BuildKit (buildx or buildctl):
# Railpack with buildx (default)
rise build myapp:latest --backend railpack
rise deployment create myproject --backend railpack
# Railpack with buildctl
rise build myapp:latest --backend railpack:buildctl
rise deployment create myproject --backend railpack:buildctl
Troubleshooting: If railpack builds fail with the error:
ERROR: failed to build: failed to solve: requested experimental feature mergeop has been disabled on the build server: only enabled with containerd image store backend
This occurs when using Docker Desktop’s default builder. Create a custom buildx builder to work around this:
docker buildx create --use
Auto-detection
When --backend is omitted, the CLI automatically detects the build method:
- If
Dockerfileexists → usesdockerbackend - If
Containerfileexists (and no Dockerfile) → usesdockerbackend - Otherwise → uses
packbackend
# Auto-detect (has Dockerfile → uses docker)
rise build myapp:latest
# Auto-detect (no Dockerfile → uses pack)
rise build myapp:latest
# Explicit backend selection
rise build myapp:latest --backend railpack
Custom Dockerfile Path
By default, Rise looks for Dockerfile or Containerfile in the project directory. Use --dockerfile to specify a different file:
# Use a custom Dockerfile
rise build myapp:latest --dockerfile Dockerfile.prod
# Use Dockerfile from subdirectory
rise build myapp:latest --dockerfile docker/Dockerfile.build
# Works with all Docker-based backends
rise build myapp:latest --backend docker:buildx --dockerfile Dockerfile.dev
In rise.toml:
[build]
backend = "docker"
dockerfile = "Dockerfile.prod"
Build Contexts (Docker/Podman Multi-Stage Builds)
Build contexts allow you to use additional directories or files in your multi-stage Docker builds. This is useful when you need to access files outside the main build context or reference other directories.
CLI Usage:
# Add a single build context
rise build myapp:latest --build-context mylib=../my-library
# Add multiple build contexts
rise build myapp:latest \
--build-context mylib=../my-library \
--build-context tools=../build-tools
# Specify custom default build context (the main context directory)
rise build myapp:latest --context ./app
# Combine with other options
rise build myapp:latest \
--backend docker:buildx \
--build-context mylib=../my-library \
--dockerfile Dockerfile.prod
In rise.toml:
[build]
backend = "docker"
dockerfile = "Dockerfile"
build_context = "./app" # Optional: custom default build context
[build.build_contexts]
mylib = "../my-library"
tools = "../build-tools"
shared = "../shared-components"
Using Build Contexts in Dockerfile:
Once defined, you can reference build contexts in your Dockerfile:
# Copy files from a named build context
FROM alpine AS base
COPY --from=mylib /src /app/lib
# Or use the context as a build stage
FROM scratch AS mylib
# This stage can access files from ../my-library
FROM node:20 AS build
# Copy from the mylib context
COPY --from=mylib /package.json /app/lib/package.json
Configuration Precedence:
- CLI
--build-contextflags override config file contexts with the same name - CLI
--contextflag overrides config filebuild_context - Default build context is the app path (project directory) if not specified
Notes:
- Build contexts are only supported by Docker and Podman backends
- Paths are relative to the
rise.tomlfile location (typically the project root directory) - Available with all Docker-based backends:
docker,docker:buildx,buildctl
Build-Time Environment Variables
You can pass environment variables to your build process using the -e or --env flag. This works consistently across all build backends:
# Pass environment variable with explicit value
rise build myapp:latest -e NODE_ENV=production
# Pass environment variable from current environment
export DATABASE_URL=postgres://localhost/mydb
rise build myapp:latest -e DATABASE_URL
# Multiple environment variables
rise build myapp:latest -e NODE_ENV=production -e API_KEY=secret123
# Works with all backends
rise build myapp:latest --backend docker -e BUILD_VERSION=1.2.3
rise build myapp:latest --backend pack -e BP_NODE_VERSION=20
rise build myapp:latest --backend railpack -e CUSTOM_VAR=value
Backend-Specific Behavior
Docker Backend:
- Environment variables are passed as
--build-argarguments to Docker build - Available in Dockerfile
ARGdeclarations andRUNcommands - Example Dockerfile usage:
ARG NODE_ENV ARG BUILD_VERSION RUN echo "Building version $BUILD_VERSION in $NODE_ENV mode"
Pack Backend:
- Environment variables are passed as
--envarguments to pack CLI - Buildpacks can read these during detection and build phases
- Common uses: configuring buildpack versions, build flags
Railpack Backend:
- Environment variables are passed as BuildKit secrets
- Available in all build steps defined in the Railpack plan
- Railpack frontend exposes them as environment variables during build
Project Configuration (rise.toml)
You can create a rise.toml or .rise.toml file in your project directory to define default build options. This allows you to avoid repeating CLI flags for every build.
Example rise.toml:
[build]
backend = "pack"
builder = "heroku/builder:24"
buildpacks = ["heroku/nodejs", "heroku/procfile"]
env = ["BP_NODE_VERSION=20"]
Configuration Precedence
Build options are resolved in the following order (highest to lowest):
- CLI flags (e.g.,
--backend pack) - Project config file (
rise.tomlor.rise.toml) - Environment variables (e.g.,
RISE_CONTAINER_CLI,RISE_MANAGED_BUILDKIT) - Global config (
~/.config/rise/config.json) - Auto-detection/defaults
Vector field behavior:
- All vector fields (
buildpacks,env): CLI values are appended to config values (merged)
This allows you to set common buildpacks and environment variables in the config file and add additional ones via CLI as needed.
Available Options
All CLI build flags can be specified in the [build] section:
| Field | Type | Description |
|---|---|---|
backend | String | Build backend: docker, docker:build, docker:buildx, buildctl, pack, railpack, railpack:buildctl |
dockerfile | String | Path to Dockerfile (relative to rise.toml location). Defaults to Dockerfile or Containerfile |
build_context | String | Default build context (docker/podman only). The path argument to docker build <path>. Defaults to rise.toml location. Path is relative to rise.toml location. |
build_contexts | Object | Named build contexts for multi-stage builds (docker/podman only). Format: { "name" = "path" }. Paths are relative to rise.toml location. |
builder | String | Buildpack builder image (pack only) |
buildpacks | Array | List of buildpacks to use (pack only) |
env | Array | Environment variables for build (format: KEY=VALUE or KEY) |
container_cli | String | Container CLI: docker or podman |
managed_buildkit | Boolean | Enable managed BuildKit daemon |
railpack_embed_ssl_cert | Boolean | Embed SSL certificate in Railpack builds |
Examples
Heroku buildpacks:
[build]
backend = "pack"
builder = "heroku/builder:24"
buildpacks = ["heroku/nodejs", "heroku/procfile"]
Railpack with SSL:
[build]
backend = "railpack"
managed_buildkit = true
railpack_embed_ssl_cert = true
Docker with build args:
[build]
backend = "docker"
env = ["VERSION=1.0.0", "NODE_ENV=production"]
Pack with custom environment:
[build]
backend = "pack"
builder = "paketobuildpacks/builder-jammy-base"
env = ["BP_NODE_VERSION=20.*"]
CLI Override
CLI flags always take precedence over project config:
# Uses docker backend despite project config specifying pack
rise build myapp:latest --backend docker
# Adds to env variables from config
# If config has env = ["NODE_ENV=production"]
# This results in: env = ["NODE_ENV=production", "API_KEY=secret"]
rise build myapp:latest -e API_KEY=secret
# Enable managed BuildKit (shorthand defaults to true)
rise build myapp:latest --managed-buildkit
# Disable managed BuildKit despite config enabling it
rise build myapp:latest --managed-buildkit=false
# Enable SSL certificate embedding (shorthand defaults to true)
rise build myapp:latest --railpack-embed-ssl-cert
File Naming
Both rise.toml and .rise.toml are supported. If both exist in the same directory, rise.toml takes precedence (with a warning).
SSL Certificate Handling (Managed BuildKit Daemon)
When building with BuildKit-based backends (docker, railpack) on macOS behind corporate proxies (Cloudflare, Zscaler, etc.) or environments with custom CA certificates, builds may fail with SSL certificate verification errors.
The Problem
BuildKit runs as a separate daemon and requires CA certificates to be available at daemon startup. This affects two scenarios:
- BuildKit daemon operations: Pulling base images, accessing registries
- Build-time operations: Application builds (RUN instructions) downloading packages, cloning repos
Solution: Managed BuildKit Daemon
Rise CLI provides an opt-in managed BuildKit daemon feature that automatically creates and manages a BuildKit daemon with SSL certificate support.
Enable via CLI flag:
# Shorthand (defaults to true)
rise build myapp:latest --backend railpack --managed-buildkit
rise deployment create myproject --backend railpack --managed-buildkit
# Explicit values
rise build myapp:latest --backend railpack --managed-buildkit=true
rise build myapp:latest --backend railpack --managed-buildkit=false
Or set environment variable:
export RISE_MANAGED_BUILDKIT=true
rise build myapp:latest --backend railpack
Or configure permanently:
# Set in config file
rise config set managed_buildkit true
How It Works
When --managed-buildkit is enabled, Rise CLI follows this priority order:
- Existing BUILDKIT_HOST: If the
BUILDKIT_HOSTenvironment variable is already set, Rise uses your existing buildkit daemon - Managed daemon: Otherwise, Rise creates a
rise-buildkitdaemon container:- With SSL certificate mounted at
/etc/ssl/certs/ca-certificates.crtifSSL_CERT_FILEis set - Without SSL certificate if
SSL_CERT_FILEis not set - Configured with
--platform linux/amd64for Mac compatibility
- With SSL certificate mounted at
- Automatic updates: If
SSL_CERT_FILEis added, removed, or changed, the daemon is automatically recreated
Warning When Not Enabled
If SSL_CERT_FILE is set but --managed-buildkit is not enabled, you’ll see a warning during builds that require BuildKit (docker, railpack):
Warning: SSL_CERT_FILE is set but managed BuildKit daemon is disabled.
Railpack builds may fail with SSL certificate errors in corporate environments.
To enable automatic BuildKit daemon management:
rise build --managed-buildkit ...
Or set environment variable:
export RISE_MANAGED_BUILDKIT=true
For manual setup, see: https://github.com/NiklasRosenstein/rise/issues/18
Note: The managed BuildKit feature works with or without SSL_CERT_FILE - it simply mounts the certificate when available.
Affected Build Backends
- ✅
pack- Already supportsSSL_CERT_FILEnatively (no managed daemon needed) - ⚠️
docker/docker:build- Does not support BuildKit secrets (usedocker:buildxinstead) - ✅
docker:buildx- Full SSL support via BuildKit secrets (auto-injected into Dockerfile) - ✅
buildctl- Full SSL support via BuildKit secrets (auto-injected into Dockerfile) - ⚠️
railpack/railpack:buildx- Benefits from managed daemon - ⚠️
railpack:buildctl- Benefits from managed daemon
Manual Setup (Advanced)
For users who prefer manual control, you can create your own BuildKit daemon:
# Start BuildKit daemon with certificate
docker run --platform linux/amd64 --privileged --name my-buildkit --rm -d \
--volume $SSL_CERT_FILE:/etc/ssl/certs/ca-certificates.crt:ro \
moby/buildkit
# Point Rise CLI to your daemon
export BUILDKIT_HOST=docker-container://my-buildkit
rise build myapp:latest --backend railpack
For more details, see Issue #18.
Build-Time SSL Certificate Embedding (Railpack)
The --railpack-embed-ssl-cert flag embeds SSL certificates directly into the Railpack build plan for use during RUN commands. This complements --managed-buildkit by handling build-time SSL requirements.
Important differences:
--managed-buildkit: Injects SSL certs into BuildKit daemon (for pulling images, registry access). Does NOT embed cert into final image.--railpack-embed-ssl-cert: Embeds SSL certs into railpack plan.json as build assets (for RUN commands during build). DOES embed cert into final image.
Both flags can be used together for comprehensive SSL support.
When to use:
- Application builds need SSL certificates (pip install, npm install, git clone, curl requests)
- Running behind corporate proxies with certificate inspection
- Custom or self-signed certificates
Default behavior:
- Automatically enabled when
SSL_CERT_FILEenvironment variable is set - This ensures builds work by default in most SSL certificate scenarios
- Can be explicitly disabled with
--railpack-embed-ssl-cert=false
Usage:
export SSL_CERT_FILE=/path/to/ca-certificates.crt
# Embedding is automatically enabled when SSL_CERT_FILE is set
rise build myapp:latest --backend railpack
# Explicitly disable even when SSL_CERT_FILE is set
rise build myapp:latest --backend railpack --railpack-embed-ssl-cert=false
# Explicitly enable (useful when SSL_CERT_FILE is not set)
rise build myapp:latest --backend railpack --railpack-embed-ssl-cert=true
# Combine with managed BuildKit for comprehensive SSL support
rise build myapp:latest --backend railpack --managed-buildkit
Environment variable support:
export RISE_RAILPACK_EMBED_SSL_CERT=true
rise build myapp:latest --backend railpack
# Embedding is enabled via env var
Config file support:
rise config set railpack_embed_ssl_cert true
rise build myapp:latest --backend railpack
# Embedding is enabled via config
Precedence order: CLI flag > Environment variable > Config file > Default (enabled if SSL_CERT_FILE is set)
Build-Time SSL Certificate Injection (Docker/Buildctl)
When using docker:buildx or buildctl backends with SSL_CERT_FILE set, Rise automatically injects SSL certificates into your Dockerfile’s RUN commands using BuildKit secrets.
How it works:
- Rise preprocesses your Dockerfile to add
--mount=type=secret,id=SSL_CERT_FILE,target=<path>to each RUN command - The secret mount makes the certificate available at multiple standard system paths during RUN commands
- The certificate is passed to BuildKit via
--secret id=SSL_CERT_FILE,src=<path> - Certificates are NOT embedded in the final image (only available during build)
Supported certificate paths:
/etc/ssl/certs/ca-certificates.crt(Debian, Ubuntu, Arch)/etc/pki/tls/certs/ca-bundle.crt(RedHat, CentOS, Fedora)/etc/ssl/ca-bundle.pem(OpenSUSE, SLES)/etc/ssl/cert.pem(Alpine Linux)/usr/lib/ssl/cert.pem(OpenSSL default)
Example:
export SSL_CERT_FILE=/path/to/ca-certificates.crt
# SSL certificates automatically available during RUN commands
rise build myapp:latest --backend docker:buildx
rise build myapp:latest --backend buildctl
# Debug logging shows the preprocessed Dockerfile
RUST_LOG=debug rise build myapp:latest --backend docker:buildx
What your Dockerfile sees:
Original:
RUN apt-get update && apt-get install -y curl
RUN pip install -r requirements.txt
Processed (internal):
RUN --mount=type=secret,id=SSL_CERT_FILE,target=/etc/ssl/certs/ca-certificates.crt --mount=type=secret,id=SSL_CERT_FILE,target=/etc/pki/tls/certs/ca-bundle.crt ... apt-get update && apt-get install -y curl
RUN --mount=type=secret,id=SSL_CERT_FILE,target=/etc/ssl/certs/ca-certificates.crt --mount=type=secret,id=SSL_CERT_FILE,target=/etc/pki/tls/certs/ca-bundle.crt ... pip install -r requirements.txt
Note: The docker:build backend does not support BuildKit secrets. If SSL_CERT_FILE is set, you’ll see a warning recommending docker:buildx instead.
Proxy Support
Rise CLI automatically detects and injects HTTP/HTTPS proxy environment variables into all build backends. This is useful when your build environment requires going through a corporate proxy to access external resources.
Supported Proxy Variables
Rise automatically detects these standard proxy environment variables:
HTTP_PROXY/http_proxyHTTPS_PROXY/https_proxyNO_PROXY/no_proxy
All variants (uppercase and lowercase) are automatically detected from your environment and passed to the appropriate build backend.
Localhost to host.docker.internal Transformation
Since builds execute in containers, localhost and 127.0.0.1 addresses are automatically transformed to host.docker.internal to allow container builds to reach a proxy server running on your host machine.
Example transformations:
http://localhost:3128→http://host.docker.internal:3128https://127.0.0.1:8080/path→https://host.docker.internal:8080/pathhttp://user:pass@localhost:3128→http://user:pass@host.docker.internal:3128http://proxy.example.com:8080→ unchanged (not localhost)
Note: NO_PROXY and no_proxy values are passed through unchanged since they contain comma-separated lists, not URLs.
Usage Examples
Set proxy variables in your environment before running rise:
export HTTP_PROXY=http://proxy.example.com:3128
export HTTPS_PROXY=http://proxy.example.com:3128
export NO_PROXY=localhost,127.0.0.1,.example.com
# Proxy settings automatically applied to all builds
rise build myapp:latest ./path
rise deployment create myproject --path ./app
With localhost proxy:
# Proxy running on your host machine
export HTTP_PROXY=http://localhost:3128
export HTTPS_PROXY=http://localhost:3128
# Automatically transformed to host.docker.internal for container builds
rise build myapp:latest --backend pack
rise build myapp:latest --backend railpack
rise build myapp:latest --backend docker
Backend-Specific Behavior
Pack Backend:
- Proxy variables are passed via
--envarguments to the pack CLI - Pack forwards these to the buildpack lifecycle containers
- Works with pack’s
--network hostnetworking mode
Railpack Backend:
- Proxy variables are passed via
--secretflags to buildx/buildctl - Secret references are added to build steps in
plan.json - BuildKit provides the secret values from environment variables
- Railpack frontend makes these available as environment variables in build steps
Docker Backend:
- Proxy variables are passed via
--build-argarguments - Docker automatically respects
HTTP_PROXY,HTTPS_PROXY, andNO_PROXYas build args - Available during Dockerfile
RUNcommands
No Configuration Required
Proxy support is completely automatic - no CLI flags or configuration needed. Rise CLI respects the standard proxy environment variables already set in your shell or CI/CD environment.
Local Development with rise run
The rise run command builds and immediately runs your application locally for development purposes. This is useful for testing your application before deploying it to the Rise platform.
Basic Usage
# Build and run from current directory (defaults to port 8080)
rise run
# Specify directory
rise run ./path/to/app
# Custom port
rise run --http-port 3000
# Expose on different host port
rise run --http-port 8080 --expose 3000
With Project Environment Variables
When authenticated, you can load non-secret environment variables from a project:
# Load environment variables from project
rise run --project my-app
Note: Only non-secret environment variables are loaded. Secret values cannot be retrieved from the backend for security reasons.
Setting Runtime Environment Variables
You can set custom runtime environment variables using the --run-env flag:
# Set a single environment variable
rise run --run-env DATABASE_URL=postgres://localhost/mydb
# Set multiple environment variables
rise run --run-env DATABASE_URL=postgres://localhost/mydb --run-env DEBUG=true --run-env API_KEY=test123
# Combine with project environment variables
rise run --project my-app --run-env OVERRIDE_VAR=custom_value
Runtime environment variables set via --run-env take precedence and can override project environment variables if they have the same key.
Build Backend Selection
Use any build backend with rise run:
# Use pack backend
rise run --backend pack
# Use docker backend
rise run --backend docker
# With custom builder
rise run --backend pack --builder paketobuildpacks/builder-jammy-base
How It Works
- Build: Builds the container image locally using the selected backend
- Tag: Tags the image as
rise-local-{project-name}(orrise-local-appif no project specified) - Run: Executes
docker run --rm -it -p {expose}:{http-port} -e PORT={http-port} {image} - Environment: Automatically sets
PORTenvironment variable - Project Variables: Loads non-secret environment variables from the project if
--projectis specified - Cleanup: Container is automatically removed when stopped (
--rmflag)
Port Configuration
--http-port: The port your application listens on inside the container (setsPORTenv var)--expose: The port exposed on your host machine (defaults to same as--http-port)
Example:
# Application listens on port 8080, accessible at http://localhost:3000
rise run --http-port 8080 --expose 3000
Interactive Mode
rise run uses interactive mode (-it) so you can:
- See real-time logs from your application
- Press Ctrl+C to stop the container
- Interact with your application if it accepts input
Complete Example
# Create a project
rise project create my-app
# Set some environment variables
rise env set my-app DATABASE_URL postgres://localhost/mydb
rise env set my-app API_KEY secret123 --secret
# Run locally with project environment variables
rise run --project my-app --http-port 3000
# Application accessible at http://localhost:3000
# PORT=3000 and DATABASE_URL=postgres://localhost/mydb are set
# API_KEY is not loaded (secret values not retrievable)
# Run with additional runtime environment variables
rise run --project my-app --http-port 3000 --run-env DEBUG=true --run-env LOG_LEVEL=verbose
# Application now has PORT, DATABASE_URL, DEBUG, and LOG_LEVEL environment variables set