Search code examples
javascripttypescriptsveltekitpm2

How to deploy SvelteKit on your own server properly without interrupting the user?


So I created this information system with Sveltekit, Ubuntu 22.04 as the server OS, PM2 as the process manager and I'm running the app in Node.Js. I've read the Sveltekit documentation on how to build and deploy the app for node adapter and here's my svelte.config.js code:

import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/kit/vite';

import path from 'path';

/** @type {import('@sveltejs/kit').Config} */
const config = {
    // Consult https://kit.svelte.dev/docs/integrations#preprocessors
    // for more information about preprocessors
    preprocess: vitePreprocess(),

    kit: {
        // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
        // If your environment is not supported or you settled on a specific environment, switch out the adapter.
        // See https://kit.svelte.dev/docs/adapters for more information about adapters.
        adapter: adapter({
            // default options are shown
            out: 'build',
            precompress: false,
            envPrefix: '',
            polyfill: true
        }),
        // Disable CSRF
        csrf: {
            checkOrigin: false,
        },
        alias: {
            // these are the aliases and paths to them
            '$src': path.resolve('./src')
        }
    },
    vitePlugin: {
        inspector: false
    }
};

export default config;

After building it, all I need to do is submit the command npm run preview in order to run the app.

Since I'm using PM2, I created an ecosystem.config.cjs file so that I can run the app with PM2 command. Here's the configuration:

module.exports = {
    apps: [
        {
            name: "app_sistamu",
            script: "npm",
            args: "run preview -- --host --port 8080",
            watch: false,
            instances: "max",
            exec_mode: "cluster"
        },
        {
            name: "app_sistamu_DEV",
            script: "npm",
            args: "run dev -- --host --port 5173",
            out_file: "/dev/null",
            watch: false
        }
    ],
};

After all the preparation, all I have to do is merging everything from development branch to master branch. Here's what I do:

git merge -m "Merging branch development to master" development; npm install; npm run build; pm2 reload ecosystem.config.cjs --only app_sistamu

With this my application can now run on the server. There's just one thing. The next time if I want to make a changes, I need to rebuild the app and then reload the PM2. I mean yeah it makes sense because in order to deploy the app I need to build it again. But this causes a problem where during the build process, the app returns a 502 Bad Gateway error. But after everything is done, the app is back to normal again without any error.

This raises a question, how to deploy the app properly without disrupting user's interaction with the app?


Solution

  • After a few attempts, I think I finally solved this problem. Thanks to a youtube video provided by auqust, here's how I solve it:

    1. Before building the app, make sure you've installed adapter-node as per the documentation on your SvelteKit project. If so, run the build command to build the app, in this example run npm run build;
    2. After done building the app, there should be a folder named "build". I then copied all the files inside it to a separate directory which is /var/www/production/. To copy all the files, simply run this command cp -a build/. /var/www/production/;
    3. Open the target directory. Initialize the npm and then add "type"="module" in package.json file. Next, add express.js package by typing in npm install -D express;
    4. Create a new file named server.js for serving the application later:
    import express from "express";
    import { handler } from "./handler.js";
    
    
    const app  = express();
    const port = 8080;
    
    app.use(handler);
    
    app.listen(port, () => {
        console.log(`The application is running on port ${port}`);
    });
    
    1. Since I'm using PM2 to run the app, I created an ecosystem.config.cjs file to run PM2:
    module.exports = {
        apps: [
            {
                name     : "app_sistamu",
                script   : "server.js",
                watch    : false,
                instances: "max",
                exec_mode: "cluster"
            }
        ],
    };
    
    

    To start PM2, I run this command pm2 start ecosystem.config.cjs --only app_sistamu.

    By doing this the next time I have to apply new changes I simply run build command, paste the build files into the targeted directory, and then I reload the PM2 instances. This method gives zero downtime, meaning there's no interruption in user's end.

    I hope this will help others in the future. If I did some mistake along the way or perhaps there's any other solution or approach, please comment down below here.