Document: WM-101 P. Webb Category: Tutorial 2026-09-02 How to migrate from Gel to Disc, in Production Abstract Unforeseen gotchas galore, but I figured it out. Body The last post/guide about this[1] only handled local development and as we know, what "works on my machine" often doesn’t translate well to other machines. In order to perform the backup of my Gel database on my server, I first needed to create the `gel-export` folder in the directory of the app Gel was running in before giving ownership and permissions to the `gel` user. ``` # run these commands as root user chown gel: gel-export chmod u+w gel-export su gel ``` `su gel` switches you from the `root` user to the `gel` user. Replace `INSTANCE` with the name of your Gel instance to get your user and password (if you don’t already know them). ``` # run this command as gel user gel instance credentials --insecure-dsn -I INSTANCE # you should see something like gel:///DATABASE?port=PORT&user=USER&password=PASSWORD&tls_ca_file=%3C...%3E&tls_security=no_host_verification ``` ``` # run this command as gel user # you will be prompted for that password you saw earlier gel dump --all --format=dir gel-export --admin --user USER --instance INSTANCE --tls-security insecure --password ``` I ran into errors complaining about missing packages so I exited back to my `root` user and ran these: ``` apt install -y postgresql-common /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh apt install -y postgresql-client-18 ``` I was able to proceed with the rest of the export process after that. When deploying my new Disc-aware program though, I found I needed two `systemd` files. The server install script[2] for Disc is designed to "just work" and will create a service file for you and have Disc running automatically but there’s another running process that’s required, and that’s after creating a new user (as `root`) and giving said user a password. I named my new user `disco` because `disc` was already taken by the install script and you cannot switch to said user, for security reasons (no login allowed). Here is my service file for the Nickel API: ``` [Unit] Description=NICKEL API Documentation=https://nickel.video After=network.target [Service] ExecStartPre=/opt/disc/bin/disc start ExecStartPre=/opt/disc/bin/disc migrate ExecStartPre=/opt/disc/bin/disc codegen ExecStart=/var/www/api/nickel Group=disco Restart=on-failure User=disco WorkingDirectory=/var/www/api [Install] WantedBy=multi-user.target ``` The Nickel API is a compiled executable. Before that starts running, I have Disc boot up, run migrations if necessary, and codegen for the compiled SDK. It’s quite possible this can be optimized. Oh, I had to ensure the `disco` user had ownership over the codegen directory (`dbschema/`) so migrations wouldn’t fail: ``` chown -R disco:disco /var/www/api/dbschema ``` Here is the other service file I had to make to serve the Disc UI and database for the API to reach. I should probably rename it to `api-serve` so it’s not confusing to future me. ``` [Unit] Description=Disc Start Server After=network.target postgresql.service api.nickel.service Wants=disc.service api.nickel.service [Service] ExecStart=/opt/disc/bin/disc serve Group=disco Restart=on-failure RestartSec=5 SyslogIdentifier=disc-start User=disco WorkingDirectory=/var/www/api [Install] WantedBy=multi-user.target ``` Once confiming the service files worked, I had to finish the data import process. ``` su disco disc migrate disc db import ./gel-export/data disc db import ./gel-export/data --on-conflict skip ``` Computed fields in my Gel export gave me issues. I made backup copies of the affected files, deleted the columns causing problems, and the import went through! Fully prepared to manually enter the data of the deleted columns, I accessed the Disc UI and was surprised to see my computed columns present! Which, makes sense in hindsight…the other data in the backup would obviously compute these fields on import. I’ll add a fox for that in Disc. --- This was quite a painful process TBQH. I was obviously motiviated to push through it but good grief! The "Happy Path" and DX needs tuning. The Disc UI doesn’t come with authentication so that you can design your own (or just disable it in production). Here’s how I handle that with Caddy: ``` # generate a password in your favorite password manager and # have Caddy hash it caddy hash-password ``` Make note of that generated hash! Then, update `basic_auth` in your Caddyfile (and make sure `PORT` matches whatever your Disc UI wants: ``` database.nickel.video { basic_auth { SOME_USERNAME } encode gzip reverse_proxy localhost:PORT root * /path/to/your/project } ``` Last step is to configure your firewall! ``` ufw allow https ufw allow ssh ufw deny PORT ufw enable ``` This makes your server reachable via HTTPS (no plain HTTP), allows you to continue connecting to your server via SSH, rejects any outside connections to your Disc UI’s port, and enables the firewall. I feel like this "tutorial" was all over the place and I certainly felt that way while I was trying to keep track of all the pieces, haha! At some point I had to uninstall Gel because I think it was interfering with Disc. Please excuse the mess, there’s more work to be done to make Disc the best, smoothest database to ever exist. 🕸️ References [1] [2]