Deploy SvelteKit with SQLite to Fly.io

I’ve been unable to find good examples of how to deploy a SvelteKit app with SQlite to Fly. So I’m sharing what I’ve created: it’s an open-source blog application called PostOwl.

Our recommended deployment target is fly.io and we have all the files in the repo on GitHub to make that happen.

Deployment instructions are here: Deploy | PostOwl

Useful files to take note of:

  • We use .env.production which means Vite and SvelteKit will build and make your environment variables available during deployment and in production. See .env.production.example
  • We have code for creating / migrating the SQLite database and shutting the application server down gracefully when auto_stop_machines = true: start-app.js is called from start-fly.sh which in turn is called from fly.toml.

On my todo list is to create a proper migration script for SQLite (we’re not using an orm).

Let me know if you have any questions or suggestions for improvements!

1 Like

I like your style. Let’s see if we can do better together!

I’d like to start by replacing as much as possible in your installation instructions with fly launch. For Node applications, fly launch makes use of GitHub - fly-apps/dockerfile-node: Dockerfile generator for Node.js, which is just a nodejs application that makes use of ejs templates.

Can we look at each change you had to make to the Dockerfile, figure out why that change was made, and if dockerfile-node could make that change automatically?

For example, the first change I note is RUN npm install dotenv. Why isn’t dotenv in your package.json? If you intend for it to not be in your package.json, is there check (or series of checks) that could be made that would cause the ejs template to add this line? For example, the presence of .env* files, perhaps coupled with vite?

By working through these changes we can make fly a better destination for svelte developers as well as making the installation instructions easier for your users. It would also position you to take advantages of improvements to the fly platform as we make them.

1 Like

Thanks Sam - that’s just the kind of feedback I was hoping for :smile:

Yep, it should be - I added it yesterday while doimg some deploy experiments. I’ll get package.json updated as a first step.

I hadn’t looked into fly launch much as my experience was it tried to infer a bunch of stuff and didn’t come up with the right answers. But I see where you going with it. Happy to work together to make it better.

It’ll be a couple of days before I can look into fly launch properly, but when I do I’ll make some updates and ask any questions here. Or feel free to make a PR to the repo if you want :smile:

@rubys I’ve optimised Dockerfile, moved dotenv to package.json and pushed the changes up to the repo.

If I remove my Dockerfile and fly.toml and run fly launch the generated files don’t work. Things that I believe need adding:

For fly.toml:

# Override the CMD set in Dockerfile so we can migrate the SQLite database
[experimental]
  cmd = ["start-fly.sh"]
  entrypoint = ["sh"]

For Dockerfile after FROM base:

RUN apt-get update -qq \
    && apt-get install -y sqlite3

We need sqlite3 installed on the final image.

More adjustments would be needed to get SvelteKit to build the app correctly as it needs the data directory present at build time and I haven’t been able to adjust my scripts and paths to work from the generated files yet (despite quite a bit ot trying)!

There’s also redundant stuff in the generated Dockerfile like:

# Install packages needed to build node modules
RUN apt-get update -qq && \
    apt-get install -y build-essential pkg-config python-is-python3

and

ENV DATABASE_URL="file:///data/sqlite.db"

and this is incorrect: CMD [ "npm", "run", "start" ] it should be CMD [ "node", "build" ] for a production SvelteKit app (though this line being wrong woudn’t break our deployment as we’re using a customer startup script to manage the SQLite database).

I see what you mean about using fly launch to make installation easier and “take advantages of improvements to the fly platform”. If we can remove the example files and just get people to fly launch and then make a couple of minor edits that would be better. But I’m struggling to achieve that right now.

A concern I have with that approach is what happens if the generated output from fly launch changes due to ‘improvements’ that mean our docs are out of date and people can’t deploy. How would I keep up-to-date with changes to what fly launch will output?

I’m actually on vacation this week, but checking in periodically. Next week I can help more. But a few points:

Try running:

npx dockerfile --add sqlite3

View the chanvges in both your Dockerfile and package.json. Commit the changes to package.json. Now everybody that runs fly launch will have that package installed.

Now imagine what other options could be useful. For example, dockerfile-rails has a --migrate option, there is no reason that dockerfile-node couldn’t also have such an option.

Depending on your project, your npm install may need to build native node modules. This enables such modules to be built. As this is part of the build step, these packages are not included in your final image.

Ideally the node build would be run during the build step instead of on every deploy. What would be better is if only the migrate and start-app.js is run on every deploy.

https://github.com/fly-apps/dockerfile-node is open source, and you are welcome to participate. This includes adding a test that shows what you expect will be output for Dockerfile and fly.toml, etc.

Thanks for replying while you’re on vacation. Please enjoy your time off and don’t worry about this thread until you’re back. :beach_umbrella:

That’s all really useful information, thank you! I’ll dig in a bit more and I’m sure I can get this project to be idiomatic with what you’re recommending.

I did some more work on this today and I believe I’ve got it down to needing as few edits as possible after running fly launch with its current abilities.

All that’s needed is

  1. In Dockerfile replace RUN npm run build with RUN mkdir /data && npm run build
  2. Add this to fly.toml:
[experimental]
  cmd = ["/app/scripts/start-fly.sh"]
  entrypoint = ["sh"]

That’s because:

  1. We need to create a /data directory for the database at build time. This is outside of the app directory so can’t be copied across.
  2. For SQLite on Fly any actions on the database need to be taken after the applicaiton is up because the database is on a volume (unlike for PostgreSQL where you can connect to the existing db and run migrations before that). I’m not aware of another way to do this, but I’m open to suggestions!

That said, this definitely simplifies our deployment instructions and we can stick with this for now.

I’ll keep on eye on future developments of dockerfile-node. Thanks for pointing me in the right direction.

Let’s see if we can do better!

Can you use --instructions-base to add mkdir -p /data? --instructions-build is unfortunately added too late. If that doesn’t work, I can always add a --instructions-prebuild.

I’ve added --cmd and --entrypoint to dockerfile.node.

Tried but didn’t add in the correct place as suggested.

Thanks for helping with this, but I couldn’t get it to generate what was needed with a straight fly launch.

I’m also looking to get LiteFS set up but couldn’t get the correct config using Dockerfile node. I’m probably being stupid but it’s quicker and more reliable for me to do things manually at this time. The hand-crafted Dockerfile I had is more optimised than what’s generated automatically. Feel free to experiment with PostOwl - it’s a standard SvelteKit app with SQLite - PRs welcome!