> ## Documentation Index
> Fetch the complete documentation index at: https://developer.upsun.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Scriberr on Upsun

> Learn how to deploy Scriberr, an AI-powered transcription tool, on Upsun for self-hosted audio and video transcription.

export const PostMeta = ({data = {}}) => {
  const {author, date, image} = data;
  const authors = Array.isArray(author) ? author : author ? [author] : [];
  const resolveAuthor = slug => {
    const entry = AUTHOR_MAP[slug] || ({});
    const name = entry.name || slug;
    const github = entry.github || null;
    const linkedin = entry.linkedin || null;
    const url = github ? `https://github.com/${github}` : linkedin || null;
    const avatarUrl = github ? `https://github.com/${github}.png?size=64` : null;
    return {
      name,
      url,
      avatarUrl
    };
  };
  const formattedDate = date ? new Date(date).toLocaleDateString('en-US', {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  }) : null;
  if (!image && authors.length === 0 && !formattedDate) return null;
  const AUTHOR_MAP = {
    "aaron-collier": {
      "name": "Aaron Collier"
    },
    "aaron-dudenhofer": {
      "name": "Aaron Dudenhofer"
    },
    "aaron-porter": {
      "name": "Aaron Porter"
    },
    "adriaan-odendaal": {
      "name": "Adriaan Odendaal"
    },
    "ajmal": {
      "name": "Ajmal Siddiqui"
    },
    "akalipetis": {
      "name": "Antonis Kalipetis"
    },
    "alexander-varwijk": {
      "name": "Alexander Varwijk"
    },
    "alicia-bevilacqua": {
      "name": "Alicia Bevilacqua"
    },
    "amelie-deguerry": {
      "name": "Amelie Deguerry"
    },
    "anacidre": {
      "name": "Ana Cidre",
      "linkedin": "https://www.linkedin.com/in/ana-cidre"
    },
    "andoni": {
      "name": "Andoni Auzmendi"
    },
    "andrei-taranu": {
      "name": "Andrei (Alex) Taranu",
      "linkedin": "https://www.linkedin.com/in/andrei-alex-taranu/"
    },
    "andrew-baxter": {
      "name": "Andrew Baxter"
    },
    "andrew-melck": {
      "name": "Andrew Melck"
    },
    "antoine-crochet-damais": {
      "name": "Antoine Crochet Damais"
    },
    "augustin-delaporte": {
      "name": "Augustin Delaporte",
      "linkedin": "https://www.linkedin.com/in/augustindelaporte/"
    },
    "branislav-bujisic": {
      "name": "Branislav Bujisic"
    },
    "carl-smith": {
      "name": "Carl Smith"
    },
    "caroline-leroy": {
      "name": "Caroline Leroy"
    },
    "cati-mayer": {
      "name": "Cati Mayer"
    },
    "catplat": {
      "name": "C Trinkwon"
    },
    "ceelolulu": {
      "name": "Celeste van der Watt"
    },
    "chadwcarlson": {
      "name": "Chad Carlson",
      "github": "chadwcarlson",
      "linkedin": "https://www.linkedin.com/in/chadwcarlson"
    },
    "chris-ward": {
      "name": "Chris Ward"
    },
    "chris-yates": {
      "name": "Chris Yates"
    },
    "christian-sieber": {
      "name": "Christian Sieber"
    },
    "christopher-lockheardt": {
      "name": "Christopher Lockheardt"
    },
    "christopher-skene": {
      "name": "Christopher Skene"
    },
    "chuck-morgan": {
      "name": "Chuck Morgan"
    },
    "corey-dockendorf": {
      "name": "Corey Dockendorf"
    },
    "crell": {
      "name": "Crell"
    },
    "damz": {
      "name": "Damz"
    },
    "dan-morrison": {
      "name": "Dan Morrison"
    },
    "davidbonachera": {
      "name": "David Bonachera",
      "github": "davidbonachera",
      "linkedin": "https://www.linkedin.com/in/davidbonachera"
    },
    "dereliahmet1": {
      "name": "Ahmet Faruk Dereli"
    },
    "devicezero": {
      "name": "Jonas Kröger",
      "github": "devicezero",
      "linkedin": "https://www.linkedin.com/in/jonaskroeger/"
    },
    "doug-goldberg": {
      "name": "Doug Goldberg"
    },
    "duncan-naves": {
      "name": "Duncan Naves",
      "github": "duncannaves",
      "linkedin": "https://www.linkedin.com/in/duncan-naves-a94423aa"
    },
    "erika-bustamante": {
      "name": "Erika Bustamante"
    },
    "fabpot": {
      "name": "Fabien Potencier"
    },
    "flovntp": {
      "name": "Florent Huck",
      "github": "flovntp",
      "linkedin": "https://www.linkedin.com/in/florenthuck"
    },
    "fred-plais": {
      "name": "Fred Plais"
    },
    "gauthier-garnier": {
      "name": "Gauthier Garnier"
    },
    "gilzow": {
      "name": "Paul Gilzow"
    },
    "gmoigneu": {
      "name": "Guillaume Moigneu",
      "github": "gmoigneu",
      "linkedin": "https://www.linkedin.com/in/guillaumemoigneu/"
    },
    "gregqualls": {
      "name": "Greg Qualls"
    },
    "guguss": {
      "name": "Augustin Delaporte"
    },
    "haylee-millar": {
      "name": "Haylee Millar"
    },
    "ivana-kotur": {
      "name": "Ivana Kotur"
    },
    "jackrabbithanna": {
      "name": "Mark Hanna"
    },
    "jared-wright": {
      "name": "Jared Wright",
      "github": "jww-sh",
      "linkedin": "https://www.linkedin.com/in/jaredwaynewright"
    },
    "jessica-orozco": {
      "name": "Jessica Orozco"
    },
    "joey-stanford": {
      "name": "Joey Stanford"
    },
    "john-grubb": {
      "name": "John Grubb"
    },
    "jonas-kruger": {
      "name": "Jonas Kruger"
    },
    "kathryn-frazer": {
      "name": "Kathryn Frazer"
    },
    "kemiojo": {
      "name": "Kemi Elizabeth Ojogbede"
    },
    "kieronsambrook-smith": {
      "name": "Kieronsambrook Smith"
    },
    "laurent-arnoud": {
      "name": "Laurent Arnoud"
    },
    "letoya-boyne": {
      "name": "Letoya Boyne"
    },
    "lolautruche": {
      "name": "Jérôme Vieilledent"
    },
    "lyly-lepinay": {
      "name": "Lyly Lepinay"
    },
    "manauwar-alam": {
      "name": "Manauwar Alam"
    },
    "marc-antoine-porri": {
      "name": "Marc Antoine Porri"
    },
    "maria-antinkaapo": {
      "name": "Maria Antinkaapo"
    },
    "maria-de-anton": {
      "name": "Maria De Anton"
    },
    "mark-dorison": {
      "name": "Mark Dorison"
    },
    "markus-hausammann": {
      "name": "Markus Hausammann"
    },
    "mary-thomas": {
      "name": "Mary Thomas"
    },
    "mathias-bolt-lesniak": {
      "name": "Mathias Bolt Lesniak"
    },
    "mathieu-strauch": {
      "name": "Mathieu Strauch"
    },
    "matthias-van-woensel": {
      "name": "Matthias Van Woensel",
      "linkedin": "https://www.linkedin.com/in/matthias-van-woensel-267a069"
    },
    "michael-sharp": {
      "name": "Michael Sharp"
    },
    "mupsi": {
      "name": "Marine Gandy"
    },
    "natalie-harper": {
      "name": "Natalie Harper"
    },
    "ngommenginger": {
      "name": "Nicolas Gommenginger",
      "linkedin": "https://www.linkedin.com/in/nicolas-gommenginger"
    },
    "nicholas-bennison": {
      "name": "Nicholas Bennison"
    },
    "nicholas-vahalik": {
      "name": "Nicholas Vahalik"
    },
    "nick-hardiman": {
      "name": "Nick Hardiman"
    },
    "nickanderegg": {
      "name": "Nickanderegg"
    },
    "nicolas-grekas": {
      "name": "Nicolas Grekas",
      "github": "nicolas-grekas",
      "linkedin": "https://www.linkedin.com/in/nicolasgrekas/"
    },
    "niti-malwade": {
      "name": "Niti Malwade"
    },
    "opensocialteam": {
      "name": "Opensocialteam"
    },
    "ori-pekelman": {
      "name": "Ori Pekelman"
    },
    "otavio-santana": {
      "name": "Otavio Santana"
    },
    "palwandi": {
      "name": "Pawan Alwandi",
      "github": "pawpy",
      "linkedin": "https://www.linkedin.com/in/pawanalwandi"
    },
    "patrick-boest": {
      "name": "Patrick Boest"
    },
    "patrick-dawkins": {
      "name": "Patrick Dawkins",
      "github": "pjcdawkins",
      "linkedin": "https://www.linkedin.com/in/patrickdawkins"
    },
    "patrick-klima": {
      "name": "Patrick Klima"
    },
    "pjcdawkins": {
      "name": "Pjcdawkins"
    },
    "prineet-kaurbhurji": {
      "name": "Prineet Kaurbhurji"
    },
    "quentin-sinig": {
      "name": "Quentin Sinig"
    },
    "ralt": {
      "name": "Florian Margaine",
      "github": "ralt",
      "linkedin": "https://www.linkedin.com/in/florian-margaine-43971136"
    },
    "ramanathanramakrishnamurthy": {
      "name": "Ramanathanramakrishnamurthy"
    },
    "remi-lejeune": {
      "name": "Rémi Lejeune"
    },
    "ribel": {
      "name": "Taras Kruts"
    },
    "robert-douglass": {
      "name": "Robert Douglass"
    },
    "rudy-weber": {
      "name": "Rudy Weber"
    },
    "ryan-hicks": {
      "name": "Ryan Hicks"
    },
    "sabri-helal": {
      "name": "Sabri Helal"
    },
    "savannah-bergeron": {
      "name": "Savannah Bergeron"
    },
    "shannon-vettes": {
      "name": "Shannon Vettes"
    },
    "shawn-ogasawara": {
      "name": "Shawn Ogasawara",
      "linkedin": "https://www.linkedin.com/in/shawn-ogasawara-83a9a0/"
    },
    "shawna-spoor": {
      "name": "Shawna Spoor"
    },
    "shedrack-akintayo": {
      "name": "Shedrack Akintayo"
    },
    "simon-ruggier": {
      "name": "Simon Ruggier"
    },
    "sophie-van-der-kindere": {
      "name": "Sophie Van Der Kindere"
    },
    "stefanos-thampis": {
      "name": "Stefanos Thampis"
    },
    "stephen-weinberg": {
      "name": "Stephen Weinberg"
    },
    "sukhman-virk": {
      "name": "Sukhman Virk"
    },
    "sumaira-nazir": {
      "name": "Sumaira Nazir"
    },
    "sumer": {
      "name": "Sümer Cip"
    },
    "syed-raza": {
      "name": "Syed Raza"
    },
    "tamara-bacchia": {
      "name": "Tamara Bacchia"
    },
    "tara-arnold": {
      "name": "Tara Arnold"
    },
    "theosakamg": {
      "name": "Mickael Gaillard",
      "github": "theosakamg"
    },
    "thomasdiluccio": {
      "name": "Thomas di Luccio"
    },
    "tim-anderson": {
      "name": "Tim Anderson"
    },
    "tom-helmer-hansen": {
      "name": "Tom Helmer Hansen"
    },
    "tylermills": {
      "name": "Tyler Mills"
    },
    "upsun": {
      "name": "Upsun"
    },
    "veronika-tolkachova": {
      "name": "Veronika Tolkachova",
      "linkedin": "https://www.linkedin.com/in/veronika-tolkachova-169167a2"
    },
    "vince-parker": {
      "name": "Vince Parker"
    },
    "vinnie-russo": {
      "name": "Vincenzo Russo"
    },
    "vrobert78": {
      "name": "Vincent Robert",
      "github": "vrobert78",
      "linkedin": "https://www.linkedin.com/in/vincent-robert-498a883"
    },
    "yuriy-babenko": {
      "name": "Yuriy Babenko"
    },
    "yuriy-gerasimov": {
      "name": "Yuriy Gerasimov"
    }
  };
  return <div className="post-meta">
      {(authors.length > 0 || formattedDate) && <div className="post-meta-info">
          {authors.length > 0 && <div className="post-meta-authors">
              {authors.map(slug => {
    const {name, url, avatarUrl} = resolveAuthor(slug);
    const inner = <>
                    {avatarUrl && <img src={avatarUrl} alt={name} className="post-meta-avatar" />}
                    <span className="post-meta-author-name">{name}</span>
                  </>;
    return url ? <a key={slug} href={url} target="_blank" rel="noopener noreferrer" className="post-meta-author">
                    {inner}
                  </a> : <span key={slug} className="post-meta-author">{inner}</span>;
  })}
            </div>}
          {authors.length > 0 && formattedDate && <span className="post-meta-separator" aria-hidden="true">·</span>}
          {formattedDate && <span className="post-meta-date">{formattedDate}</span>}
        </div>}
      {image && <img src={image} alt="" className="post-meta-image" aria-hidden="true" />}
    </div>;
};

<PostMeta data={{ author: ["ralt"], date: "2026-02-10T10:54:00.000Z", image: "/images/posts/hands-on/scriberr-on-upsun/scriberr-on-upsun.webp" }} />

Scriberr is an AI-powered media transcription and subtitle generation tool. It automatically transcribes video and audio content using advanced speech recognition models, making your content more accessible and searchable.

In this tutorial, you'll deploy a fully working Scriberr instance on Upsun. By the end, you'll have your own private transcription service that you control.

## Prerequisites

Before you begin, make sure you have:

* An [Upsun account](https://auth.upsun.com/register)
* The [Upsun CLI](https://docs.upsun.com/administration/cli.html) installed and authenticated
* [Git](https://git-scm.com/) installed on your local machine

**Resource note**: Scriberr requires substantial resources. You'll need a container with at least 10GB RAM and 40GB disk space. Keep this in mind when reviewing your Upsun plan.

## Why self-host your transcription service?

Commercial transcription services charge per minute of audio. If you're transcribing large amounts of content, those costs add up fast. Self-hosting gives you predictable monthly costs regardless of how much you transcribe.

Privacy is another consideration. When you use external services, your audio files leave your infrastructure. With a self-hosted solution, your recordings never leave your control. This matters for sensitive content like internal meetings, legal recordings, or medical dictation.

You also gain full control over your data. Keep transcriptions as long as you want, organize them however you prefer, and integrate with your own systems without API limitations.

## Understanding the resource requirements

Scriberr runs AI models locally for transcription. These models need significant memory to load and run. The minimum requirement is 10GB RAM, and you'll want 40GB of disk space for the models, temporary files during processing, and your transcription library.

This configuration uses CPU inference rather than GPU. GPU would be faster, but CPU costs less and works well for Scriberr's workflow. You upload files, then check back later for results. The processing time is longer, but you're not waiting in real-time.

The `HIGH_MEMORY` container profile in Upsun allocates the necessary resources for AI workloads. Expect higher hosting costs compared to a typical web application due to these requirements.

## Current limitations

Scriberr is a single-user application with basic access control. You set up an admin username and password on first use. All subsequent access requires those credentials. There's no support for multiple user accounts.

If multi-user support matters to you, consider contributing to the [Scriberr project](https://github.com/rishikanthc/Scriberr).

## Project setup

Start by cloning the example repository:

```bash theme={null}
git clone https://github.com/ralt/scriberr-upsun.git
cd scriberr-upsun
```

The repository structure is minimal. The only configuration you need is in `.upsun/config.yaml`. Scriberr itself gets downloaded during the build process.

## Understanding the Upsun configuration

Here's the complete configuration file:

```yaml {filename=".upsun/config.yaml"} theme={null}
applications:
  scriberr:
    container_profile: HIGH_MEMORY
    type: python:3.11
    hooks:
      build: |
        set -ex
        curl -L https://github.com/rishikanthc/Scriberr/releases/download/v1.2.0/Scriberr_Linux_x86_64.tar.gz | tar xz
    variables:
      env:
        APP_ENV: production
        SECURE_COOKIES: false
        XDG_DATA_HOME: /app/data
        XDG_CONFIG_HOME: /app/data
    dependencies:
      python3:
        uv: "*"
    mounts:
      data:
        source: storage
        source_path: data
      nltk_data:
        source: storage
        source_path: nltk_data
    web:
      upstream:
        socket_family: tcp
        protocol: http
      commands:
        pre_start: ln -s /app/data/uv /tmp/uv
        start: ./scriberr
      locations:
        /:
          passthru: true

routes:
  https://{default}/:
    type: upstream
    upstream: scriberr:http
```

Let's break down the key sections.

### Container profile

```yaml theme={null}
container_profile: HIGH_MEMORY
```

The `HIGH_MEMORY` profile provides the memory allocation needed for AI inference. Standard profiles don't provide enough RAM for the speech recognition models.

### Build hook

```yaml theme={null}
hooks:
  build: |
    set -ex
    curl -L https://github.com/rishikanthc/Scriberr/releases/download/v1.2.0/Scriberr_Linux_x86_64.tar.gz | tar xz
```

During build, this downloads the Scriberr v1.2.0 binary from GitHub releases and extracts it. The binary includes everything needed to run the application.

### Environment variables

```yaml theme={null}
variables:
  env:
    APP_ENV: production
    SECURE_COOKIES: false
    XDG_DATA_HOME: /app/data
    XDG_CONFIG_HOME: /app/data
```

These configure Scriberr's runtime behavior. The `XDG_*` variables direct application data to a persistent mount so your configuration and transcriptions survive deployments.

### Mounts

```yaml theme={null}
mounts:
  data:
    source: storage
    source_path: data
  nltk_data:
    source: storage
    source_path: nltk_data
```

Two persistent mounts store your data between deployments:

* `data`: Stores your transcriptions, configuration, and downloaded AI models
* `nltk_data`: Stores Natural Language Toolkit data used for text processing

### Web configuration

```yaml theme={null}
web:
  upstream:
    socket_family: tcp
    protocol: http
  commands:
    pre_start: ln -s /app/data/uv /tmp/uv
    start: ./scriberr
```

Scriberr runs as an HTTP server. The `pre_start` command creates a symlink that works around a temporary directory issue with uv, the Python package manager. The `start` command runs the Scriberr binary.

## Deploying to Upsun

With the configuration in place, create your Upsun project:

```bash theme={null}
upsun project:create
```

Follow the prompts to name your project and select a region. Then push your code to deploy:

```bash theme={null}
git push upsun main
```

The first deployment takes several minutes. Upsun downloads the Scriberr binary and builds your container. Subsequent deployments are faster since the build cache persists.

Watch the deployment logs to track progress. When you see the deployment succeed message, your application is ready.

## Accessing Scriberr

Get your application URL:

```bash theme={null}
upsun url --primary
```

The first time you access Scriberr, expect a slow response. Scriberr downloads the speech recognition models on first use. This happens once and the models persist in your data mount. Depending on the model size, this initial download takes a few minutes.

After the download completes, you'll see the Scriberr interface. You can then upload audio or video files for transcription.

## Conclusion

You now have a private transcription service running on your own infrastructure. Upload audio or video files, and Scriberr generates accurate transcriptions using AI speech recognition.

Your transcriptions stay on your infrastructure. You control the data retention and access. And your costs are predictable regardless of transcription volume.

For more details on Scriberr's features and configuration options, see the [Scriberr documentation](https://scriberr.app/).

<Note>
  Ready to deploy your own AI-powered services? [Start your free Upsun trial](https://upsun.com/) and get your applications running in minutes.
</Note>
