Deploy a Qwik App

Qwik (opens in a new tab) is a feature-rich web framework focused on performance and modularity.

This guide explains how to deploy a Qwik application to Koyeb using Git-driven deployment. The application can be built using either native buildpacks or from a Dockerfile.

To successfully follow this documentation, you will need to have a Koyeb account (opens in a new tab). You can optionally install the Koyeb CLI if you prefer to follow this guide without leaving the terminal.

You can deploy and preview the Qwik application from this guide using the Deploy to Koyeb button below.

Deploy to Koyeb (opens in a new tab)

You can consult the repository on GitHub (opens in a new tab) to find out more about the example application that this guide uses.

Create the Qwik app

To create a minimal Qwik application, you'll need Node.js (opens in a new tab) and npm installed on your local computer.

Generate a new Qwik project by typing:

npm create qwik@latest

The package manager will pull the latest version of Qwik and prompt you for information to configure your new application:

View prompts example

[main 52c2d12] add express
 5 files changed, 6457 insertions(+)
 create mode 100644 adapters/express/vite.config.ts


    .::: :--------:.
   .::::  .:-------:.
  .:::::.   .:-------.
  ::::::.     .:------.
 ::::::.        :-----:
 ::::::.       .:-----.
  :::::::.     .-----.
   ::::::::..   ---:.
    .:::::::::. :-:.

┌  Let's create a  Qwik App  ✨ (v1.2.17)

◇  Where would you like to create your new project? (Use '.' or './' for current directory)
│  ./example-qwik

●  Creating new project in  /tmp/qwik/example-qwik  ... 🐇

◇  Select a starter
│  Basic App

◇  Would you like to install npm dependencies?
│  Yes

◇  Initialize a new git repository?
│  Yes

◆  Finishing the install. Wanna hear a joke?
◇  Finishing the install. Wanna hear a joke?
│  Yes

◇  🙈 ─────────────────────────────────────────────────────╮
│                                                          │
│  How many tickles does it take to make an octopus laugh  │
│  Ten tickles.                                            │
│                                                          │

◇  App Created 🐰

◇  Git initialized 🎲

◇  Installed npm dependencies 📋

◇  Result ──────────────────────────────────────────────────╮
│                                                           │
│  🦄  Success!  Project created in example-qwik directory  │
│                                                           │
│  🤍 Integrations? Add Netlify, Cloudflare, Tailwind...    │
│     npm run qwik add                                      │
│                                                           │
│  📄 Relevant docs:                                        │
│     https://qwik.builder.io/docs/getting-started/         │
│                                                           │
│  💬 Questions? Start the conversation at:                 │
│     https://qwik.builder.io/chat                          │
│     https://twitter.com/QwikDev                           │
│                                                           │
│  👀 Presentations, Podcasts and Videos:                   │
│     https://qwik.builder.io/media/                        │
│                                                           │
│  🐰 Next steps:                                           │
│     cd example-qwik                                       │
│     npm start                                             │
│                                                           │
│                                                           │

└  Happy coding! 🎉

To set up a basic application, choose the following options:

  • Where would you like to create your new project? ./example-qwik
  • Select a starter: Basic App
  • Would you like to install npm dependencies? Yes
  • Initialize a new git repository? Yes
  • Finishing the install. Wanna hear a joke? Your call :)

Once the installation is complete, move into the new directory:

cd example-qwik

The new project directory contains the following files and directories:

  • node_modules/: The project dependencies.
  • package.json: The standard Node.js project configuration, defining metadata, scripts, dependencies, etc.
  • package-lock.json: The lock file for the project dependencies.
  • public/: The project's static assets like images, etc.
  • README.md: A basic README telling you how to work with the project.
  • src/components/: A location to place reusable components for the project.
  • src/entry.dev.tsx: The entry point for running the application in development mode.
  • src/entry.preview.tsx: The entry point for running the application in preview mode, which performs a local production build and serves it using a very basic server.
  • src/entry.ssr.tsx: The entry point for running the application when not using in-browser rendering.
  • src/global.css: The CSS file for rules that apply to the entire application.
  • src/media/: Additional media files.
  • src/root.tsx: The entry point for the application itself.
  • src/routes/: The directory-based routing files for Qwik City (opens in a new tab) applications.
  • tsconfig.json: The TypeScript compiler configuration for the project.
  • vite.config.ts: The Vite configuration used to build and serve the project.

The primary application files are all stored within the src directory, while much of the project configuration is defined at the root level.

Test the Qwik application locally

Now that the project is initialized, we can test it locally by running the included development server. From inside your project directory, type:

npm run start

By default, this will start up a local development server and then automatically open a new tab in your browser to the page. The application will automatically reload when any changes are detected within the project.

Press CTRL-C to stop the development server when you are finished.

Add the Express server adapter

Qwik has the ability to install and configure deployment adapters to help Vite build your application for a specific target. We will use this to install Express (opens in a new tab) to serve our project in production.

To start adding an adapter, type:

npm run qwik add

Qwik will prompt you to select which integration you'd like to add. Select Adapter: Node.js Express Server to continue.

Confirm your choice to add the new dependencies to your project. During this process, Qwik will also update the README with new information about deploying to production and add new deployment scripts to the package.json file.

Modify the project start script

Currently, when you run npm run start for the project, it starts up the project in development mode instead of serving the production build as many Node.js projects do. Koyeb tries to run the start script from the package.json file when deploying Node.js projects, we need to modify this to reflect the correct command.

Open the package.json file in your text editor:

  "scripts": {
    "build": "qwik build",
    "build.client": "vite build",
    "build.preview": "vite build --ssr src/entry.preview.tsx",
    "build.server": "vite build -c adapters/express/vite.config.ts",
    "build.types": "tsc --incremental --noEmit",
    "deploy": "echo 'Run \"npm run qwik add\" to install a server adapter'",
    "dev": "vite --mode ssr",
    "dev.open": "vite --open --mode ssr",
    "dev.debug": "node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force",
    "fmt": "prettier --write .",
    "fmt.check": "prettier --check .",
    "lint": "eslint \"src/**/*.ts*\"",
    "preview": "qwik build preview && vite preview --open",
    "serve": "node server/entry.express",
    "start": "node server/entry.express",
    "qwik": "qwik"

Inside, replace the value for the start script with the one used by the serve script. If you'd like to preserve the functionality for the current start script, you can optionally add a dev.open script with that behavior.

Create a Dockerfile for the project (Optional)

We can build and run our Qwik project on Koyeb using the native Node.js buildpack, but we can also optionally build from a Dockerfile for more control. To make it possible to build a container image for our application, we just need to create the Dockerfile. We'll also define a .dockerignore file to tell the builder what files to skip when creating the image.

Start by defining the .dockerignore file in your main project directory. Inside, paste the following contents:


This file tells Docker to not include Git files, the Docker files themselves, dependency directories, built artifacts, or environment files unless explicitly called out within the Dockerfile. This helps ensure that the image we build is not bloated and that the build completes faster.

Next, create a new file called Dockerfile within the main project directory. Inside, paste the following contents, adapted from this NestJS template (opens in a new tab):

# Build stage
FROM node:lts-alpine AS builder
USER node
WORKDIR /home/node
COPY package*.json .
RUN npm ci
COPY --chown=node:node . .
RUN npm run build
# Final run stage
FROM node:lts-alpine AS runner
ENV NODE_ENV production
USER node
WORKDIR /home/node
COPY --from=builder --chown=node:node /home/node/package*.json .
COPY --from=builder --chown=node:node /home/node/node_modules ./node_modules
COPY --from=builder --chown=node:node /home/node/dist ./dist
COPY --from=builder --chown=node:node /home/node/server ./server
EXPOSE ${PORT:-3000}
CMD ["node", "server/entry.express"]

This Dockerfile uses a multistage build (opens in a new tab) to separate the build steps from the final image environment. This creates a more streamlined image and allows us to tightly control what files are included in the final image.

Both stages are based on the Alpine version of the base Node.js image (opens in a new tab) and both configure a dedicated user called node to perform the build and run actions. The application is built and run from /home/node, the node user's home directory, to make permissions management more straightforward.

The build stage is configured to copy the package.json and package-lock.json files and install the dependencies. It then copies the remaining files and builds the project.

The production stage sets the NODE_ENV environment variable to production to tell Node that it should run with production features enabled. It then copies the package.json and package-lock.json files, the dependencies, and the built artifacts from the build stage. It marks the PORT environment as user configurable and exposes the port. Afterwards, it executes the same command that the start script defines in the package.json file.

If you have Docker installed locally, you can build and test the image on your computer and optionally upload it to a registry. You can deploy container images from any container registry to Koyeb.

We can also build the Dockerfile directly from the repository when we deploy, which is useful as a way of automatically deploying when changes occur. We will demonstrate this method as one of the options in this guide.

Push the project to GitHub

Once you're finished configuring the project locally, you can commit your work and push it to GitHub.

Qwik automatically initialized a Git repository when the npm create qwik@latest command was run, so we just need to add our changes to it. Run the following commands within the project's root directory to commit the project files and push changes to GitHub.

Remember to replace the values of <YOUR_GITHUB_USERNAME> and <YOUR_REPOSITORY_NAME> with your own information:

git add :/
git commit -m "Add project files"
git remote add origin git@github.com:<YOUR_GITHUB_USERNAME>/<YOUR_REPOSITORY_NAME>.git
git push -u origin main

Deploy to Koyeb using git-driven deployment

Once the repository is pushed to GitHub, you can deploy the Qwik application to Koyeb. Any changes in the deployed branch of your codebase will automatically trigger a redeploy on Koyeb, ensuring that your application is always up-to-date.

To deploy the Qwik app on Koyeb using the control panel (opens in a new tab), follow the steps below:

  1. Click Create App in the Koyeb control panel.
  2. Select GitHub as the deployment option.
  3. Choose the GitHub repository and branch containing your application code. Alternatively, you can enter our public Qwik example repository (opens in a new tab) into the Public GitHub repository at the bottom of the page: https://github.com/koyeb/example-qwik
  4. Choose the builder for your project. We can use either a Dockerfile or buildpack for this repository.
  5. Name your service, for example qwik-service.
  6. Name the App, for example example-qwik.
  7. Click the Deploy button.

A Koyeb App and Service will be created. Your application will be built and deployed to Koyeb. Once the build has finished, you will be able to access your application running on Koyeb by clicking the URL ending with .koyeb.app.

Deploy to Koyeb from a container registry

If you chose to build a container image for the Qwik application, you can optionally deploy the application from a container registry instead of from GitHub.

To deploy a pre-built Qwik container image on Koyeb using the control panel (opens in a new tab), follow the steps below:

  1. Click Create App in the Koyeb control panel.
  2. Select Docker as the deployment option.
  3. Choose the container image and tag from your registry and click Next to continue.
  4. Name your service, for example qwik-service.
  5. Name the App, for example example-qwik.
  6. Click the Deploy button.

A Koyeb App and Service will be created. The container image will be pulled from the container registry and a container will be deployed from it. Once the initialization is complete, you will be able to access your application running on Koyeb by clicking the URL ending with .koyeb.app.