Build types: buildpacks vs Dockerfile

Every app has a build type. buildpacks (auto-detect via heroku-style buildpacks) is the default and works for almost everything. dockerfile uses a Dockerfile in your repo root.

The two options

buildpacksdockerfile
Auto-detectYes — based on files in your repo (package.json, Gemfile, etc.)You supply the Dockerfile
Build envherokuish — same buildpacks Heroku usesVanilla docker build
ProcfileRequired for non-trivial appsOptional — CMD/ENTRYPOINT works
Build-time envApp config vars are exposed during buildUse ARG / build-time secrets explicitly
SpeedPer-deploy build on the stackPer-deploy build on the stack OR pre-built image
Best forStandard apps in standard languagesCustom toolchains, multi-stage builds, native deps

Setting it

$ ownstack app build-type <app> buildpacks
$ ownstack app build-type <app> dockerfile

Or set "build_type" when creating the app.

Buildpack auto-detect

Herokuish picks the first matching buildpack. The standard set covers Ruby, Node, Python, Go, PHP, Java, Clojure, Scala. Order is determined by file presence:

  • Gemfile → Ruby
  • package.json → Node.js
  • requirements.txt / Pipfile / pyproject.toml → Python
  • go.mod → Go
  • composer.json → PHP

To override, set BUILDPACK_URL as a config var to a buildpack git URL or comma-separated list.

Example: stack a Node + Ruby app

$ ownstack config:set --app=<app> \
    BUILDPACK_URL='https://github.com/heroku/heroku-buildpack-nodejs.git,https://github.com/heroku/heroku-buildpack-ruby.git'

Useful for Rails apps that need Node for asset compilation.

Dockerfile

Drop a Dockerfile in your repo root. Set build-type to dockerfile on the app. Dokku runs docker build on the stack and serves the resulting image.

Your Dockerfile must:

  • End with a CMD or ENTRYPOINT that binds to $PORT (for the web process).
  • Not hardcode a port in EXPOSE — dokku ignores it and assigns dynamically.
  • Be self-contained: no host volumes, no networks the stack doesn't manage.

Minimal Dockerfile (Node static site)

FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
CMD ["nginx", "-g", "daemon off;"]

Dokku writes a nginx.conf for you that listens on $PORT; if you supply your own, it must too.

Switching mid-life

You can switch build types on an existing app. The next deploy rebuilds from scratch with the new build type. Watch out for:

  • Persistent volumes — if you mounted any, they're reattached.
  • Linked services (Postgres, etc.) — unchanged.
  • Config vars — unchanged.