Skip to content

Deploying a project

This covers the deploy flow that's the same for every project type — connecting a repo, setting environment variables, watching a build, and redeploying on push — followed by what each of the 8 project types (Next.js, Node.js, Python, React/Vite, Static, PHP, WordPress, Docker) expects from your code. For the very first project, see Getting started.

The deploy flow

  1. Create the project and pick its type (Projects → New Project). The type decides which build/runtime template Vexlyx uses — see per-type notes below.
  2. Get code in — either connect a Git repository, or (for the Docker type) write/upload a Dockerfile directly in the dashboard.
  3. Build — Vexlyx runs Nixpacks (or, for Docker projects, a plain docker build) to detect your framework and produce a container image. Watch it live in the project's Build tab.
  4. Deploy — on a successful build, the image is brought up behind Traefik and becomes reachable at https://<project-name>.<your-domain> immediately, with HTTPS and no DNS setup needed on your part.
  5. Redeploy — either click "Deploy" again in the dashboard, or push a commit to the connected branch if you've set up auto-deploy (below).

Connect a repository

In the project's Git settings, enter the repository URL (GitHub, GitLab, or Bitbucket) and branch.

  • Public repos — paste the HTTPS URL and connect directly.
  • Private repos — click Generate SSH Key first. Vexlyx generates an Ed25519 key pair for the project and shows you the public key; add it as a Deploy Key in your repository's settings (GitHub: Settings → Deploy keys), then connect using the SSH URL (git@github.com:user/repo.git).

Auto-deploy on push

Once a repo is connected, the project's Git settings show a webhook URL and a secret. Add them as a webhook in your repository (GitHub: Settings → Webhooks → Add webhook — payload URL and secret from the dashboard, content type application/json, event: just the push event). From then on, every push to the connected branch triggers a build and redeploy automatically; pushes to other branches are acknowledged and ignored. The secret can be rotated from the same Git settings panel at any time, which immediately invalidates the old one.

Environment variables

Set them in the project's Environment tab, one at a time or by pasting/importing a whole .env file. Values are encrypted at rest (AES-256-GCM) and masked in the UI until you reveal them. They're injected as real environment variables into the running container on every deploy; for frameworks that inline variables into the client bundle at build time (Next.js's NEXT_PUBLIC_*, Vite's VITE_*), they're also passed into the build step itself, so you don't need to redeploy twice after adding one.

Watching logs

  • The Build tab streams the build log live as it happens (and replays history if you open it mid-build or after it finishes).
  • The Runtime Logs panel on a running project streams docker logs --follow for the container — useful for watching a crash loop or debugging startup errors.

Per-type notes

TypeDetected byInternal portStart command
Next.jsnext.config.*, or next in package.json3000npm run start (or your lockfile's package manager)
Node.jsAny other package.json (Express, Fastify, plain Node, etc.)3000From package.json's start script, or Nixpacks' default
Pythonmanage.py (Django), flask/fastapi in dependencies8000gunicorn/uvicorn, auto-resolved from your entrypoint
React (Vite)vite.config.* + react/@vitejs/plugin-react80Static build served by Caddy
StaticPlain HTML/CSS, or Vite with Vue/Svelte/no framework80Static build served by Caddy
PHPindex.php/composer.json, or artisan (Laravel), bin/console (Symfony)80Nginx + PHP-FPM
WordPresswp-config.php/wp-content/, or the 1-click installer80Nginx + PHP-FPM, wp-content persisted across redeploys
DockerA Dockerfile in the repo (or project type set to Docker)First EXPOSEd port, or your overrideWhatever your Dockerfile's CMD/ENTRYPOINT says

Next.js

Works with SSR, API routes/route handlers, SSG/ISR, and next/image out of the box. The package manager is picked from your lockfile (pnpm-lock.yaml, yarn.lock, bun.lock(b), or package-lock.json). Build cache (.next/cache, node_modules/.cache) is preserved between deploys, so later builds are usually 50–80% faster than the first. See the Next.js runtime reference for the full detection and compose template details.

Node.js

Any repo with a package.json that isn't recognized as Next.js, React/Vite, or another framework is treated as a plain Node app. Make sure your app listens on process.env.PORT (not a hardcoded port) and binds to 0.0.0.0, not localhost — otherwise Traefik can't reach it inside the container network.

Python

Django, Flask, and FastAPI are auto-detected and started correctly without configuration:

  • Django — needs manage.py; runs collectstatic during build, then gunicorn <module>.wsgi:application.
  • Flask — needs flask as a dependency; entrypoint resolved from app.py/main.py/wsgi.py and started with gunicorn.
  • FastAPI — needs fastapi as a dependency; entrypoint resolved similarly and started with uvicorn.

Pin your Python version with .python-version, runtime.txt, or requires-python in pyproject.toml if you need something other than the default. See the Python runtime reference for exact detection rules per framework.

React (Vite) and other static sites

Vite apps (React, Vue, Svelte, or plain) build to dist/ and are served as static files with client-side routing fallback already configured, so React Router / TanStack Router work on a hard refresh. VITE_* env vars are baked into the bundle at build time. If your build outputs somewhere other than dist/, set NIXPACKS_SPA_OUTPUT_DIR in the project's environment variables. Plain HTML/CSS sites with no package.json are also supported and served the same way.

PHP, Laravel, and Symfony

Generic PHP, Laravel (artisan), and Symfony (bin/console) are all auto-detected, with the right web root (/app/public for Laravel/Symfony) and PHP version (from composer.json, .php-version, runtime.txt, or an env var — defaulting to 8.2) picked automatically. Pretty URLs work without extra .htaccess setup.

WordPress

Create a project with type WordPress and use the dashboard's WordPress panel to 1-click install: it downloads WordPress core, generates secure salts, and wires up wp-config.php against a database you provide. wp-content (uploads, themes, plugins) is mounted as a persistent volume, so it survives redeploys. Plugin and theme ZIPs can be uploaded directly from the same panel. See PHP & WordPress runtime reference for the installer details and API.

Docker (custom Dockerfile)

For anything the above don't cover — Go, Rust, Bun, or a stack with unusual build steps — create a project of type Docker and write (or paste) a Dockerfile in the project's Dockerfile panel; starter templates are available for common stacks. Vexlyx runs a plain docker build, reads your EXPOSE directive to know which port to route traffic to, and passes your project's environment variables in as --build-args. A .dockerignore is supported the same way it would be locally.

Next steps

Released under the MIT license.