---
title: "Deploy a Dart App on Railway, Part 1"
description: "Learn how to build and deploy a Dart app to Railway"
date: 2024-12-13T00:00:00.000Z
updated: 2026-10-29T11:13:12.522Z
topics: ["Guides"]
tags: []
url: https://railway.com/guides/deploy-a-dart-app-part-1
---

# Deploy a Dart App on Railway, Part 1

Have you ever been on a trip with friends and found yourself in a heated trivia competition?

No ….? Ok … no problem

However, if your answer was yes, then the rest is for you!

That's what happened to me recently. While waiting to reach our destination, me and my friends started a "who knows best" game. It was fun and exciting, but I quickly realized there was room for improvement.

The problem? No one was keeping track of the score, and we kept arguing over the correct answers.

As such, and as a programmer, I decided to make an app

![Pro-gamer move](https://cms.railway.com/media/ec9fe4ec4d1a0a44ac27dcdf2c4a4c5676bfd7a79ac3e5060a3994fd3a0bd020.png)

## The End Goal 🏆

The goal is simple, to make an app that everyone can use to play trivia games. 

- All players see the same question at the same time and everyone has a chance to answer.
- After all players submit their answers, the app will show the correct answer on the host device, whether or not each player answered correctly on their devices.
- A bonus is added to players who answer quicker than others.
- After all questions are answered by all players, the final result is shown on the host, and the rank of each player is shown on their device.
- Players can sign up for an account to be able to create games and host them

![Overview of app flow](https://cms.railway.com/media/03febdf09d8e46fa3af299b4c85649ce831e5482e8fc0652fc324a7963a1d387.png)

## Tech stack 🛠️

### Frontend

For this tutorial, we’ll be using [Flutter](https://flutter.dev/) for the frontend with [rxdart](https://pub.dev/packages/rxdart) streams for realtime communications and [Riverpod](https://riverpod.dev/) for dependency injection.

### Backend

And for the backend, I didn’t really know which Dart package/framework to choose. So I did a small research and decided to go with [Serverpod](https://serverpod.dev/). Why? glad you asked:

- **Typesafe Dart**: Serverpod allows you to call functions from your Flutter app as if they were in another file, it doesn’t feel like you’re calling some flaky string function name with flaky json, it’s all typesafe Dart from client to server
- **Better Developer Experience:** Serverpod offers many features that makes backend development very efficient. Things like automatic code generation, built-in ORM, migrations and logging

In addition, it has built in support for authentication and data streaming, which we need in this project to build realtime multiplayer experience

## Initial project setup 📁

So, and without further ado, let’s get started and create a new Flutter + Serverpod project

### Serverpod CLI

- First off, download the Serverpod CLI

```bash
dart pub global activate serverpod_cli
```

### New Serverpod project

- Create a new Serverpod project and open it in your preferred code editor

```dart
serverpod create trivyal
cd trivyal
```

### Project structure

If this is your first time using Serverpod, check out their [documentation](https://docs.serverpod.dev/get-started) to understand Serverpod project structure

## Railway setup 🚄

Let’s get our project setup in Railway!

### Adding a database

- Head to [dev.new](http://dev.new/) to create a new project on Railway
- Select `Deploy PostgreSQL` from the options

![Selecting PostgresQL from New Project prompt](https://cms.railway.com/media/a51e870df48ba7af0b79aac2e9a6d21235059d95b303c774666914eab1485906.png)

- After the database is deployed, you can go to settings and enable [App Sleeping](https://docs.railway.com/deployments/serverless) so our elephant doesn’t get sleep deprived

### Running first database migration

- Edit the `trivyal_server/config/production.yaml` file to match the deployed railway db
- In the settings tab under Networking, copy the provided TCP proxy host and port number
- Update the values in `trivyal_server/config/production.yaml`

The provided host should end with `.proxy.rlwy.net` 

![Here the public networking proxy hostname is autorack.proxy.rlwy.net and the provided port is 46821](https://cms.railway.com/media/86c6ed756325981bb689b7977d676195181002a83160c4998e842440817dfebb.png)

This is how your database section should look like in `production.yaml`:

```yaml
database:
  host: postgres.railway.internal # Or railway proxy provided hostname (something.proxy.rlwy.net)
  port: 46821
  name: railway
  user: postgres
  requireSsl: true
```

- Go to `trivyal_server/config/passwords.yaml` file and change the database password to match the deployed database password

<aside>
⚠️

**Warning:** Never commit your plain text passwords to git

</aside>

- Now that serverpod has all the info to connect to our database, we’re ready to run the first migration using:

```dart
dart bin/main.dart --mode production --apply-migrations
```

Make sure to run the previous command in the `trivyal_server` directory

### **Railway Up**

- Install the Railway CLI using your [preferred method](https://docs.railway.com/cli#installing-the-cli), for example using Homebrew:

```
brew install railway
```

- Authenticate your CLI session:

```
railway login
```

- Link to the project and service:

```
railway link
```

- Finally, use Railway up to deploy the code:

```
railway up -d
```

Railway will automatically detect the `Dockerfile` at the root of the project and automatically build and deploy our app

### Adding flutter web build

The provided Dockerfile at `trivyal_server/Dockerfile` only builds the server directory, but doesn’t add the flutter web build to the static hosting

To fix this:

- Move the `trivyal_server/Dockerfile` to the root of our monorepo
- Edit `Dockerfile` so it installs Flutter SDK, makes a web build and copies the build to our `trivyal_server/web` directory

```docker
FROM dart:stable AS build

WORKDIR /server
COPY trivyal_server .

# Build our server
RUN dart pub get
RUN dart compile exe bin/main.dart -o bin/server

WORKDIR /web
COPY trivyal_flutter/ .
# Copy our client package code. The flutter app depends on it
COPY trivyal_client/ ../trivyal_client/

# Install Flutter SDK
RUN apt-get update && apt-get -y install curl git unzip xz-utils zip libglu1-mesa
RUN git clone https://github.com/flutter/flutter.git /usr/local/flutter
ENV PATH="${PATH}:/usr/local/flutter/bin"

# Run web build
RUN flutter pub get
RUN flutter build web

FROM alpine:latest

ENV runmode=production
ENV serverid=default
ENV logging=normal
ENV role=monolith

# Copy dart runtime and build artifacts
COPY --from=build /runtime/ /
COPY --from=build /server/bin/server server
COPY --from=build /server/config/ config/
COPY --from=build /server/web/ web/
COPY --from=build /server/migrations/ migrations/

COPY --from=build /web/build/web/ web/

EXPOSE 8080
EXPOSE 8081
EXPOSE 8082

ENTRYPOINT ./server --mode=$runmode --server-id=$serverid --logging=$logging --role=$role
```

*If you named your project differently, you should update the directories’ names in the previous Dockerfile*

- Remove the default Serverpod index template in `trivyal_server/web` and its renderer in `trivyal_server/lib/src/web`
- Update `trivyal_server/lib/server.dart` file:

```dart
import 'package:serverpod/serverpod.dart';

import 'package:trivyal_server/src/web/routes/root.dart';

import 'src/generated/protocol.dart';
import 'src/generated/endpoints.dart';

// This is the starting point of your Serverpod server. In most cases, you will
// only need to make additions to this file if you add future calls,  are
// configuring Relic (Serverpod's web-server), or need custom setup work.

void run(List<String> args) async {
  // Initialize Serverpod and connect it with your generated code.
  final pod = Serverpod(
    args,
    Protocol(),
    Endpoints(),
  );

  // If you are using any future calls, they need to be registered here.
  // pod.registerFutureCall(ExampleFutureCall(), 'exampleFutureCall');

  // Serve all files in the trivyal_server/web directory.
  // This directory will be populated by Docker with our flutter web build.
  pod.webServer.addRoute(
    RouteStaticDirectory(
      serverDirectory: '.',
      serveAsRootPath: '/index.html',
      basePath: '/',
    ),
    '/*',
  );

  // Start the server.
  await pod.start();
}
```

### Deploying the server

- Go back to Railway dashboard and press (`Ctrl+K` or `Command+K`) to open the Railway command palette
- Choose to deploy a new empty service

![Choosing new empty service](https://cms.railway.com/media/c82d3d1996e687a797efde4401300444b84eef723c1e1faa7793cfef47fa33c6.png)

### Environment Variables

To make our server able to connect to our postgres database, we need to setup environment variables. Don’t worry no need to go back and forth between both services, as Railway provides [variables references](https://docs.railway.com/variables#referencing-another-services-variable) that allow us to reference variables from our postgres service to our server

- Go to Variables tab and paste this JSON in the Raw editor:

```json
{
  "SERVERPOD_DATABASE_USER": "${{Postgres.POSTGRES_USER}}",
  "SERVERPOD_DATABASE_PASSWORD": "${{Postgres.POSTGRES_PASSWORD}}",
  "SERVERPOD_DATABASE_HOST": "${{Postgres.RAILWAY_PRIVATE_DOMAIN}}",
  "SERVERPOD_DATABASE_PORT": "5432",
  "SERVERPOD_DATABASE_NAME": "${{Postgres.POSTGRES_DB}}",
  "SERVERPOD_DATABASE_REQUIRE_SSL": "true"
}
```

- Also, go to service settings and enable [App Sleeping](https://docs.railway.com/deployments/serverless) so our server can sleep at network traffic nights
- Finally, click Deploy changes or press `Shift+Enter`

Railway will detect the `Dockerfile` at the root of the project and automatically build and deploy our app.

### Caddy reverse proxy

Because Serverpod is modular and you may not need to use all the features at once, by default the api server listens on port 8080 and the webserver listens on 8082

Since we can use Railway’s provided domain for only one port (for now), we would need to split the http traffic somehow to port 8080 and port 8082 for the api and the static webserver respectively

Luckily, we can use this ready-to-delpoy [Serverpod reverse proxy template](https://railway.com/new/template/fiqpE3)

- Press `Ctrl+K` or `Command+K` and choose to deploy a new service from a template
- From the templates list, choose **Serverpod reverse proxy**: *Unite your frontend & serverpod backend under one domain*

![Selecting the Serverpod reverse proxy](https://cms.railway.com/media/22b7746a745a0b05d134090f3e80a70b456bc3947049143974b83bf15499904c.png)

- If you kept all Serverpod’s default config, the api server should be at port 8080 and the webserver at port 8082. The domain should be the Serverpod service private domain
So your Caddy service environment variables should look like this:

```json
{
  "BACKEND_PORT": "8080",
  "FRONTEND_PORT": "8082"
  "BACKEND_DOMAIN": "${{trivyal.RAILWAY_PRIVATE_DOMAIN}}",
  "FRONTEND_DOMAIN": "${{trivyal.RAILWAY_PRIVATE_DOMAIN}}",
}
```

### Change the domain

- After the reverse proxy is deployed, go to Networking section and change the domain name to anything you like

![How your canvas should look like at the end of Railway backend setup](https://cms.railway.com/media/73288234879fd7dc80059e3ac6cb71393690130bf07ef436ad620334ce511705.png)

### The moment of truth

To make sure everything is working, go to your caddy proxy’s domain. You should see something like this:

![Testing that your Serverpod service is working](https://cms.railway.com/media/e8aa11439af881debda7dbba2eacd4ee89404db97ebfa055592b81004fce7b17.png)

And if you make a get request to `/api/example/hello?name=world`, it should return “Hello world”

```bash
$ curl "https://trivyal.up.railway.app/api/example/hello?name=world"
"Hello world"%
```

If that is not the case, you might need to check the server logs in Railway

Anyways, that was just part 1 of Deploying a Dart app to Railway, if you want to continue reading, you should checkout [part 2](https://railway.com/guides/deploy-a-dart-app-part-2)

## Useful Resources 📖

Here are some helpful resources if you have any trouble.

- [Railway Hello Serverpod Template](https://railway.com/new/template/SJzxFe?referralCode=androidquartz)
- [GitHub repo](https://github.com/Andrew-Bekhiet/hello_serverpod)
- [Railway Docs](https://docs.railway.com/)
- [Serverpod Docs](https://docs.serverpod.dev/)