Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/pages/tutorials/[category]/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ if (!page.value) {
</UPageHeader>

<UPageBody>
<div
v-if="page!.body"
class="content prose mb-8"
>
<ContentRenderer :value="page!" />
</div>
<TutorialsArticles :path="route.path" />
</UPageBody>
</UPage>
Expand Down
2 changes: 1 addition & 1 deletion content/getting-started/2.create-a-project.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ The project that runs from this `docker-compose.yml` file is not production-read
We also have a number of guides on self-hosting Directus on various cloud providers, like Amazon Web Services, Microsoft Azure, and Google Cloud Platform.

::callout{icon="material-symbols:school-outline" color="secondary" to="/tutorials/self-hosting"}
See how to deploy Directus on multiple hosting providers.
See how to deploy Directus (all options).
::

## Next Steps
Expand Down
4 changes: 4 additions & 0 deletions content/self-hosting/1.overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ On top of the Directus license, you will need to consider the cost of your infra

And, of course, the cost of time to manage and maintain all of these moving parts. In some contexts, this is a non-issue, but in others, it can be a significant factor.

::callout{icon="material-symbols:school-outline" color="secondary" to="/tutorials/self-hosting"}
How to deploy Directus: see all options (Cloud, Docker, and platform-specific guides).
::

### Directus Cloud

Directus Cloud is a hosted version of Directus that is maintained by the Directus team. It is a fully managed service that provides a secure and scalable environment for your Directus project.
Expand Down
2 changes: 1 addition & 1 deletion content/self-hosting/3.deploying.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ navigation:
Directus is provided as a Docker image. This means you can deploy it on many different platforms. While each is slightly different, the core concepts are the same.

::callout{icon="material-symbols:school-outline" color="secondary" to="/tutorials/self-hosting"}
See all vendor-specifc self-hosting deployment tutorials.
How to deploy Directus (all methods).
::

## Environment Variables
Expand Down
92 changes: 92 additions & 0 deletions content/tutorials/6.self-hosting/deploy-directus-to-railway.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
---
id: 7c8e9f0a-1b2c-4d5e-8f9a-0b1c2d3e4f5a
slug: deploy-directus-to-railway
title: Deploy Directus to Railway
technologies:
- railway
description: Deploy Directus on Railway using the official one-click templates (blank instance or CMS template) with PostgreSQL, Redis, and S3-compatible storage.
---

Railway is a platform that runs your app and its dependencies (databases, cache, storage) in one place. You don't manage servers or containers yourself; you deploy from a template or connect a repo, set variables, and Railway handles the rest.

We maintain two one-click templates on Railway:

- **[Directus (blank)](https://railway.com/deploy/directus-official)** A fresh Directus instance with PostgreSQL, Redis, and S3-compatible bucket storage provisioned by the template. Use this when you want full control over your data model from scratch.
- **[Directus CMS](https://railway.com/deploy/directus-cms)** The same stack with our [CMS template](https://github.com/directus-labs/Directus-Railway-CMS-Template) applied: pre-configured collections for pages, articles, categories, authors, and related fields. Ideal for content-focused projects or when you're pairing Directus with a frontend starter (Next.js, Nuxt, Astro, SvelteKit).

Choose the template that fits, then follow the steps below. The flow is the same for both; only the starting template differs.

## Prerequisites

- A [Railway](https://railway.app) account (sign up with GitHub or email). Railway templates generally require a paid account (Hobby or Pro plan) for deployment. You can also use them during your free trial of a paid plan.

## Step 1: Deploy from a template

1. Open either [Directus (blank)](https://railway.com/deploy/directus-official) or [Directus CMS](https://railway.com/deploy/directus-cms) and click **Deploy** (or **Deploy Now**).
2. If prompted, log in or create a Railway account. Railway will create a new **project** and add the template's services to it. A project is a group of services (Directus, database, Redis, bucket) that talk to each other on Railway's private network.
3. While the project is being set up, Railway may ask you to fill out some environment variables. For the **Directus CMS** template in particular, you'll typically be prompted for an admin email and password so the template can create your first admin user and apply the CMS schema. Enter values there, or you can add or change them later in the **Variables** tab (see Step 3).
4. Wait for the initial deploy to finish. In the project dashboard you'll see services such as **Directus**, **PostGIS** (PostgreSQL), **Redis**, and **Bucket**. Railway builds and runs the Directus image and wires it to the database, cache, and storage using variables it injects for you.

You don't need to clone a repo or run Docker yourself; the template defines the stack and Railway runs it.

## Step 2: Find your Directus service and URL

1. In the left sidebar of the project, click the **Directus** service (the one that runs the app, not PostGIS or Redis).
2. Open the **Settings** tab. Under **Networking**, you'll see **Public Networking**. Enable **Generate Domain** if it isn't already on. Railway will assign a URL like `your-service.up.railway.app`.
3. Copy that URL. You'll use it as `PUBLIC_URL` so Directus knows its public address (needed for login redirects, emails, and assets).

If you prefer a custom domain later, you can add it in the same **Networking** section; Railway will provision SSL for it.

## Step 3: Set environment variables

The templates preconfigure most of what Directus needs: database, Redis, and (when present) S3-compatible storage are wired up via Railway's internal references. You may still need to fill in or adjust some variables.

1. In the left sidebar, select the **Directus** service.
2. Open the **Variables** tab. Check that values like `PUBLIC_URL` (your app's public URL, e.g. `https://your-directus.up.railway.app`) are set correctly. For the **Directus CMS** template only, admin email and password are usually requested during the initial deploy (Step 1); you can also set or change `ADMIN_EMAIL` and `ADMIN_PASSWORD` here. The blank template does not use admin env vars; you create the first admin user in the UI when you first visit (Step 4).
3. If you plan to use the [frontend starters](https://github.com/directus-labs/starters) (Next.js, Nuxt, Astro, SvelteKit) with the **Directus CMS** template, there are additional variables to configure for that integration; see the template or starter docs for details.

After you save variables, Railway will redeploy the Directus service so the new values take effect. The first time you open your Directus URL, you'll either see the onboarding screen (blank template) or the login screen (CMS template, using the credentials you set).

::callout{icon="material-symbols:info-outline" title="Preconfigured variables"}
The official templates set database, Redis, and (with the Bucket service) S3 storage automatically. Only override those if you're using an external database or storage; otherwise focus on `PUBLIC_URL`. For the CMS template, you can also set `ADMIN_EMAIL` and `ADMIN_PASSWORD` if you didn't during deploy. The blank template creates the first admin via the UI, not env vars.
::

## Step 4: Log in and verify

1. Open your Directus URL in a browser (the one you set as `PUBLIC_URL`).
2. **Blank template:** The first time you visit, you'll see the Directus onboarding screen. Create your first admin user there (email and password); no environment variables are required for that flow.
3. **Directus CMS template:** Log in with the admin email and password you provided during the deploy (Step 1) or set in the **Variables** tab.
4. If you used the **Directus CMS** template, you should see the pre-configured collections (e.g. pages, articles, categories). If you used the blank template, you'll start with an empty project and can create collections as usual.

Uploads are stored in the template's S3-compatible bucket when the Bucket service is present, so file uploads will persist across redeploys.

## Optional: Custom domain and SSL

1. In the **Directus** service, go to **Settings** → **Networking**.
2. Under **Custom Domain**, add your domain (e.g. `directus.yourdomain.com`).
3. Railway shows the CNAME (or A record) you need. Add that record in your DNS provider.
4. Railway provisions SSL for the custom domain automatically. Once DNS has propagated, update `PUBLIC_URL` to the new domain (e.g. `https://directus.yourdomain.com`) so Directus uses it in links and redirects.

## Optional: Importing an existing database

If you're moving from another host (including Directus Cloud), you can import a PostgreSQL dump into the PostGIS service.

1. In the project, click the **PostGIS** (or PostgreSQL) service.
2. Open the **Connect** or **Data** tab and copy the connection string or host/port/user/password. Enable **TCP Proxy** in the service settings if you need to connect from your machine (e.g. with `psql` or a GUI).
3. From your computer, run your restore command with that connection string, for example:

```bash
# For a plain SQL dump:
psql "postgresql://user:password@host:port/railway" -f your_dump.sql

# For a custom-format dump from pg_dump -Fc:
pg_restore -d "postgresql://user:password@host:port/railway" --clean --if-exists your_dump.dump
```

Use the exact connection string Railway provides (host may be a proxy host if TCP proxy is enabled).

4. Restart the **Directus** service so it picks up the existing data. Log in with an admin user that already exists in the imported database, or set `ADMIN_EMAIL` and `ADMIN_PASSWORD` to create a new admin if the previous one was tied to the old environment.

## Summary

You deployed Directus on Railway by choosing one of our templates, letting Railway provision Postgres, Redis, and (optionally) S3-compatible storage, and setting a few required variables. The dashboard and Variables tab are where you'll adjust configuration and see logs; redeploys happen automatically when you change variables or the template updates. For more on running Directus in production, see [Deploying Directus](/self-hosting/deploying) and [Configuration](/configuration/general).
27 changes: 26 additions & 1 deletion content/tutorials/6.self-hosting/index.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
---
title: Self-Hosting
title: How to Deploy Directus
description: Deploy Directus with Directus Cloud, self-host with Docker, or follow step-by-step guides for your preferred cloud provider.
---

There are several ways to deploy Directus. This page outlines the main options: Directus Cloud (fully managed), self-hosting with Docker on your own infrastructure, and vendor-specific tutorials for popular cloud platforms.

## Directus Cloud

Directus Cloud provides infrastructure from the team who builds Directus. It is a fully managed service that handles data storage, hosting, updates, and scalability so you can focus on building your digital apps and experiences. Projects can be created in over 15 global deployment regions and feature autoscaling for improved availability. You can get a project running in about 90 seconds.

:cta-cloud

::callout{icon="material-symbols:school-outline" color="secondary" to="/cloud/getting-started/introduction"}
Learn more about Directus Cloud.
::

## Self-Host with Docker

Directus is provided as a Docker image, so you can deploy it on many different platforms. While each is slightly different, the core concepts are the same. You control the database, cache, and file storage. For a local or deployable setup, start with [Create a project](/getting-started/create-a-project). For environment variables, persistence, and production concepts, see [Deploying Directus](/self-hosting/deploying).

::callout{icon="material-symbols:school-outline" color="secondary" to="/getting-started/create-a-project"}
Create a project with Docker.
::

## Platform-Specific Guides

We also have step-by-step guides for self-hosting Directus on various cloud providers, including Amazon Web Services, Microsoft Azure, Google Cloud Platform, DigitalOcean, Ubuntu, and Railway. The tutorials below walk you through deploying on each platform (often including reverse proxy, SSL, and running as a service where relevant).
30 changes: 23 additions & 7 deletions server/api/tutorialimg.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getRequestURL } from "h3";
import sharp from "sharp";
import { getRequestURL } from 'h3';
import sharp from 'sharp';

async function getImageBuffer(baseURL: string, imagePath: string) {
const imageUrl = `${baseURL}/docs/img/tutorials/${imagePath}.png`;
Expand All @@ -12,13 +12,29 @@ async function getImageBuffer(baseURL: string, imagePath: string) {
return Buffer.from(await res.arrayBuffer());
}

async function getImageBufferOrFallback(baseURL: string, imagePath: string, fallback: string) {
const imageUrl = `${baseURL}/docs/img/tutorials/${imagePath}.png`;
const res = await fetch(imageUrl);
if (res.ok) {
return Buffer.from(await res.arrayBuffer());
}
const fallbackUrl = `${baseURL}/docs/img/tutorials/${fallback}.png`;
const fallbackRes = await fetch(fallbackUrl);
if (!fallbackRes.ok) {
throw createError({ statusCode: 404, statusMessage: 'Image not found' });
}
return Buffer.from(await fallbackRes.arrayBuffer());
}

export default defineEventHandler(async (event) => {
const query = getQuery(event);
const logoFileNames = query?.logos?.split(', ') || ['directus']; // default to directus.png
const logoFileName = logoFileNames[0];
const baseImageBuffer = await getImageBuffer(getRequestURL(event).origin, 'background');
const logoContainerBuffer = await getImageBuffer(getRequestURL(event).origin, 'logo-container');
const logoBuffer = await getImageBuffer(getRequestURL(event).origin, `${logoFileName}`);
const logosParam = typeof query?.logos === 'string' ? query.logos : '';
const logoFileNames = logosParam ? logosParam.split(', ') : ['directus'];
const logoFileName = logoFileNames[0] ?? 'directus';
const baseURL = getRequestURL(event).origin;
const baseImageBuffer = await getImageBuffer(baseURL, 'background');
const logoContainerBuffer = await getImageBuffer(baseURL, 'logo-container');
const logoBuffer = await getImageBufferOrFallback(baseURL, logoFileName, 'directus');

const [baseMetadata, logoMetadata, directusMetadata] = await Promise.all([
sharp(baseImageBuffer).metadata(),
Expand Down