zeabur-dockerfile
Zeabur Dockerfile Generation
Always use
npx zeabur@latestto invoke Zeabur CLI. Never usezeaburdirectly or any other installation method. Ifnpxis not available, install Node.js first.
When deploying source code to Zeabur, you must generate a Dockerfile. This skill covers how to analyze a project and produce a correct, deployable Dockerfile.
Prerequisites
Before generating a Dockerfile, read the key project files:
| Language | Files to read |
|---|---|
| Node.js | package.json, lockfile (package-lock.json / yarn.lock / pnpm-lock.yaml) |
| Python | requirements.txt, Pipfile, pyproject.toml |
| Go | go.mod |
| Rust | Cargo.toml |
| PHP | composer.json |
| Ruby | Gemfile |
| Java | pom.xml, build.gradle |
| .NET | *.csproj, *.sln |
| Elixir | mix.exs |
Also check for:
- Existing
Dockerfileordocker-compose.yml - Build config (
vite.config.js,webpack.config.js,next.config.js, etc.) .env.examplefor required environment variables
What NOT to read — skip these to save tokens:
- HTML files — static assets, never affect deployment logic
- Lockfile contents — only check which lockfile exists (to determine package manager). Do NOT parse the contents.
- Markdown files — documentation only, never influences Dockerfile decisions
Workflow
1. Analyze the Project
From the prerequisite files, determine:
- Programming language and version
- Framework (if any)
- Package manager
- Build command
- Start command
- Port the app listens on
- Static site vs server-side app
2. Generate the Dockerfile
General Rules
- No comments in the Dockerfile
- No
ENVorARGinstructions (unless required by the framework — see language-specific sections) - Add
LABEL "language"="<lang>"— possible values:nodejs,python,go,static,ruby,java,php,rust,dotnet,elixir,swift,bun - Add
LABEL "framework"="<framework>"only for actual web frameworks (see list below) - Always
COPY . .before running any install/build commands - Set
WORKDIR /srcunless the user specifies otherwise - Add
EXPOSEfor the port the app listens on - Do not use multi-stage builds unless the build image differs from the runtime image (e.g., build with Node.js, serve with
zeabur/caddy-static)
Framework vs Package — Only These Get a Framework Label
Frameworks (use LABEL "framework"="..."):
vite, create-react-app, next.js, remix, nuxt.js, umi, nest.js, hexo, vitepress, astro, sli.dev, docusaurus, nitropack, hono, medusa, svelte, flask, django, fastapi, spring-boot, laravel, thinkphp, rails, aspnet, blazorwasm, elysia, baojs
Never label as framework (these are packages/libraries):
- Python: gradio, pandas, numpy, requests, matplotlib, scikit-learn, tensorflow, pytorch, opencv, pillow, beautifulsoup4, selenium
- Node.js: express, koa, hapi, fastify, socket.io, moment, lodash, axios
- Other: bootstrap, jquery, chart.js, three.js, d3.js
If unsure whether something is a framework or package, do NOT add the framework label.
Static Sites — zeabur/caddy-static
For pure static websites (Vite, Astro static, Docusaurus, plain HTML), use zeabur/caddy-static:
FROM node:22-slim AS build
LABEL "language"="nodejs"
LABEL "framework"="vite"
WORKDIR /src
COPY . .
RUN npm install
RUN npm run build
FROM zeabur/caddy-static
COPY /src/dist /usr/share/caddy
Rules for zeabur/caddy-static:
- Copy build output to
/usr/share/caddy - Do NOT add
CMDorENTRYPOINT— the image handles it - It listens on port
8080 - Only use for truly static sites. Do NOT use for SSR frameworks (Next.js, Nuxt.js, SvelteKit, etc.)
- If building with Node.js then serving as static, set
LABEL "language"="nodejs"(not"static")
Node.js
- Default base image:
node:22-slim - Determine package manager by lockfile presence:
| Lockfile | Package manager | Install command |
|---|---|---|
package-lock.json |
npm | npm install |
yarn.lock |
yarn | yarn install |
pnpm-lock.yaml |
pnpm | RUN npm install -g pnpm && pnpm install |
- When using
npm, usenpm install. Do NOT usenpm cior flags like--only=production,--omit=dev,--frozen-lockfile. - For Vite static sites (React, Vue static builds), use
zeabur/caddy-staticto serve. - For Vite with SSR frameworks (SvelteKit), use Node.js runtime.
Next.js — Zeabur injects PORT=8080 into the container, so Next.js production mode listens on 8080 by default. No extra config needed.
FROM node:22-slim
LABEL "language"="nodejs"
LABEL "framework"="next.js"
WORKDIR /src
COPY . .
RUN npm install
RUN npm run build
EXPOSE 8080
CMD ["npm", "start"]
Svelte / SvelteKit — Use node:22 (not alpine, not slim). Set ENV PORT=8080. Single-stage build — do NOT use zeabur/caddy-static. Do NOT use cross-env ADAPTER=static or adapter-specific build commands.
FROM node:22
LABEL "language"="nodejs"
LABEL "framework"="svelte"
ENV PORT=8080
WORKDIR /src
RUN npm install -g pnpm@9
COPY . .
RUN pnpm install
RUN pnpm build
EXPOSE 8080
CMD ["pnpm", "start"]
Python
- Default base image:
python:3.10
Flask — Find the WSGI entry first. For example, if main.py contains app = Flask(__name__), the entry is main:app.
FROM python:3.10
LABEL "language"="python"
LABEL "framework"="flask"
WORKDIR /src
COPY . .
RUN pip install -r requirements.txt gunicorn
EXPOSE 8080
CMD ["gunicorn", "--bind", "0.0.0.0:8080", "main:app"]
FastAPI — Choose the start method based on what exists:
- If
fastapi-cliis inrequirements.txt→fastapi run - If
if __name__ == "__main__":exists in a.pyfile →python <file>.py - Otherwise →
uvicorn main:app --host 0.0.0.0 --port 8080(installuvicornif missing)
FROM python:3.10
LABEL "language"="python"
LABEL "framework"="fastapi"
WORKDIR /src
COPY . .
RUN pip install -r requirements.txt
EXPOSE 8080
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]
Generic Python — If WSGI might be applicable, use gunicorn. If unsure, just use python <file>.py.
Go
FROM golang:1.23 AS build
WORKDIR /src
COPY . .
RUN go build -o /app .
FROM debian:bookworm-slim
LABEL "language"="go"
COPY /app /app
EXPOSE 8080
CMD ["/app"]
PHP (Laravel / Symfony / Generic)
Uses NGINX + PHP-FPM. Adjust PHP version and extensions as needed.
FROM php:8.3-fpm
LABEL "language"="php"
WORKDIR /var/www
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN chmod +x /usr/local/bin/install-php-extensions && sync
RUN apt update && apt install -y cron curl gettext git grep libicu-dev nginx pkg-config unzip && rm -rf /var/lib/apt/lists/*
RUN install-php-extensions @composer apcu bcmath gd intl mysqli opcache pcntl pdo_mysql sysvsem zip
RUN cat <<'NGINX' > /etc/nginx/sites-enabled/default
server {
listen 8080;
root /var/www;
index index.php index.html;
charset utf-8;
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ /\.(?!well-known).* { deny all; }
error_log /dev/stderr;
access_log /dev/stderr;
}
NGINX
RUN chown -R www-data:www-data /var/www
COPY . /var/www
USER www-data
RUN if [ -f composer.json ]; then composer install --optimize-autoloader --classmap-authoritative --no-dev; fi && if [ -f package.json ]; then npm install; fi
USER root
EXPOSE 8080
CMD ["sh", "-c", "php-fpm -D && nginx -g 'daemon off;'"]
For Laravel, add optimization after composer install:
RUN php artisan config:cache && php artisan route:cache && php artisan view:cache
3. Handle Existing Dockerfiles
- Analyze the existing Dockerfile first
- Use it as-is if it looks correct
- Suggest improvements only if there are issues (wrong port, missing dependencies, etc.)
- Always add Zeabur-specific labels (
language,framework) if missing
CLI Commands
After generating the Dockerfile, deploy with:
npx zeabur@latest deploy --project-id <project-id> --json
For redeployment (must pass service ID to avoid creating duplicates):
npx zeabur@latest deploy --project-id <project-id> --service-id <service-id> --json
Use the zeabur-deploy skill for the full deployment workflow.
Error Handling
If the build or runtime fails:
- Check build logs with the
zeabur-deployment-logsskill - Common issues:
- Missing dependencies — add to
RUNinstall step - Wrong port — verify
EXPOSEmatches what the app listens on; check withzeabur-port-mismatchskill - Wrong start command — verify
CMDmatches the project's actual entry point - Static site served as SSR — switch to
zeabur/caddy-staticif the app produces static output - SSR served as static — switch to Node.js runtime if the app requires a server
- Missing dependencies — add to
- Fix the Dockerfile, then redeploy with
--service-idto update the existing service
More from zeabur/zeabur-claude-plugin
zeabur-deployment-logs
Use when viewing service runtime or build logs. Use when user says "show logs", "why did deploy fail", "check build output", or "debug runtime error".
54zeabur-template
Use when creating, editing, validating, or troubleshooting a Zeabur template YAML. Use when converting docker-compose to Zeabur template. Do NOT use for deploying templates (use zeabur-template-deploy instead).
53zeabur-restart
Use when restarting a Zeabur service. Use when user says "restart", "reboot service", or "service is stuck/frozen".
53zeabur-domain-url
Use when services need public URL for redirects or CORS. Use when WEB_URL or similar has trailing slash issues. Use when user reports "redirect goes to wrong URL", "CORS error", or "trailing slash problem". Also use when user says "add domain", "set up domain", "bind domain", "create domain", or "manage domains" for a Zeabur service.
52zeabur-variables
Use for ALL Zeabur environment variable operations — create, list, update, delete, or troubleshoot. Use when user says "set env var", "add variable", "create variable", "update variable", "delete variable", "change env var", or "why is my variable empty". Also use when variables are empty or SERVICE_NOT_FOUND errors.
51zeabur-update-service
Use when modifying service config without full redeploy. Use when updating env vars and restarting single service. Use when user says "change env var", "update config", "fix variable without redeploying", "upgrade service version", "update image tag", or "change service tag".
49