Vultr DocsLatest Content

How to Deploy a Next.js Application with Vultr Supabase Marketplace App

Updated on 14 October, 2025
Deploy and configure a self-hosted Supabase backend on Vultr with Next.js, Nginx, SSL, and secure PostgreSQL integration.
How to Deploy a Next.js Application with Vultr Supabase Marketplace App header image

Supabase is an open-source Backend-as-a-Service (BaaS) platform built on PostgreSQL. It offers database management, authentication, real-time subscriptions, file storage, edge functions, and a robust API layer, letting you focus on application logic instead of backend infrastructure. Its modular architecture scales easily from small prototypes to enterprise-grade applications.

When paired with the Vultr Marketplace Application, you can deploy a self-hosted, production-ready Supabase instance in minutes on a server you manage. This guide walks you through setting up the Supabase backend on a Vultr server, configuring the database and access rules, connecting a frontend application, and setting up a custom domain with SSL certificates. A simple Next.js notes app is used as an example to demonstrate the process. However, the instructions can be applied to most web projects with minimal modification.

Deploy the Vultr Marketplace Application for Supabase

Deploying Supabase on Vultr is quick and seamless. You can provision a new Vultr Compute instance from the Vultr Customer Portal by selecting your preferred server type, region, and plan, then switching to Marketplace Apps and choosing Supabase as the image. During setup, configure essential parameters such as SSH access, the hostname, and enable Limited User Login with sudo privileges under Additional Features for improved security. Once the instance is live, you can connect via SSH to perform environment configuration. For a complete, step-by-step deployment walkthrough, see How to Use Vultr’s Supabase Marketplace Application.

Note
When provisioning the instance programmatically:
  • Vultr API or Terraform: Set image_id to supabase to use the Supabase Marketplace Application.
  • Vultr CLI: Use --image "supabase" with vultr-cli instance create command.

Configure Supabase Environment

After deploying the Supabase Marketplace Application, the next step is to configure the environment with secure API keys. Supabase uses an ANON_KEY for client-side operations and a SERVICE_ROLE_KEY for server-side tasks that require elevated privileges. While default keys are generated during deployment, it is recommended to regenerate them using the JWT secret provided in the instance’s App Instructions. This ensures secure communication between your frontend and the Supabase instance by replacing the weak default keys.

To regenerate the keys, follow the steps outlined in the Update Supabase API Keys section of the How to Use Vultr’s Supabase Marketplace Application guide. After generating the new keys, update the .env file in your Supabase Docker directory with the new values, and restart the containers to apply the changes.

Configure Your Domain Name and Set up Nginx as a Reverse Proxy

By default, the application is accessible directly via your instance's IP address. However, configuring a custom domain and setting up Nginx as a reverse proxy enables you to access both your application and Supabase services through a unified domain, ensuring proper CORS handling and providing a professional deployment foundation. While this guide uses a Next.js notes application as an example, a similar configuration can be applied to any web application.

Add DNS Records

The following steps establish the minimal DNS configuration required for your domain to work with your Vultr Cloud Compute instance.

  1. Purchase or use an existing domain name from any domain registrar (e.g., Namecheap, GoDaddy, or Cloudflare).

  2. In your domain registrar’s DNS management console or by using Vultr DNS, create A records that point to the public IP address of your Vultr instance.

    • Root domain (@):

      Record Type: A
      Name/Host: @
      Value: YOUR_VULTR_INSTANCE_IP_ADDRESS
      TTL: 300
    • Subdomain (www):

      Record Type: A
      Name/Host: www
      Value: YOUR_VULTR_INSTANCE_IP_ADDRESS
      TTL: 300

    This ensures both yourdomain.com and www.yourdomain.com point to your instance.

  3. Save the DNS configuration and allow 5-60 minutes for global DNS propagation.

  4. Verify DNS propagation by testing domain resolution.

    console
    $ nslookup yourdomain.com
    

    The above command should return your Vultr instance's IP address, confirming successful DNS configuration.

Install and Set Up Nginx

  1. Update the package index to ensure access to the latest software versions.

    console
    $ sudo apt update
    
  2. Install Nginx.

    console
    $ sudo apt install nginx -y
    
  3. Start Nginx immediately so it can serve requests.

    console
    $ sudo systemctl start nginx
    
  4. Enable Nginx to start automatically after system reboots.

    console
    $ sudo systemctl enable nginx
    
  5. Verify that Nginx is active and running.

    console
    $ sudo systemctl status nginx
    

    The output should show active (running) status, indicating successful installation.

  6. Create an Nginx configuration file for your domain. Replace yourdomain.com with your actual domain name:

    console
    $ sudo nano /etc/nginx/sites-available/yourdomain.com
    
  7. Add the following server configuration. Replace yourdomain.com and ports to match your setup.

    ini
    server {
        listen 80;
        server_name yourdomain.com www.yourdomain.com;
    
        # Proxy requests to your application
        location / {
            proxy_pass http://127.0.0.1:3000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_cache_bypass $http_upgrade;
        }
    
        # Proxy Supabase API requests
        location /supabase/ {
            proxy_pass http://127.0.0.1:8000/;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_cache_bypass $http_upgrade;
        }
    }
    

    This configuration defines two proxy rules:

    * `/` routes to the application running on port `3000`.
    * `/supabase/` routes to the Supabase API running on port `8000`.

    If your application listens on a different port, update the proxy_pass directive accordingly. To expose additional Supabase services such as Auth or Realtime, add separate proxy blocks pointing to their respective ports. Save the file and exit the editor when finished.

  8. Enable the site by creating a symbolic link.

    console
    $ sudo ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/
    
  9. Test the Nginx configuration for syntax errors.

    console
    $ sudo nginx -t
    

    If the configuration is valid, you should see a message confirming the syntax is ok and the test is successful.

  10. Reload Nginx to apply the new configuration.

    console
    $ sudo systemctl reload nginx
    
  11. Verify domain and proxy configuration by testing connectivity.

    console
    $ curl -I http://yourdomain.com
    

    An HTTP response from Nginx confirms that the domain and proxy configuration are working. A 502 Bad Gateway error is expected at this stage because the frontend application is not running and is not listening on the configured port. When the application runs, Nginx forwards requests successfully, and the error disappears.

Set Up the Project

This section is dedicated to project setup and configuration. It covers preparing the backend database, defining tables and access policies, and connecting the frontend application to Supabase. The Notes App serves as an example to demonstrate how to structure tables, implement row-level security, configure environment variables, and connect a frontend to the backend. For your own project, follow the same steps while customizing tables, policies, and frontend configuration to match your application’s requirements.

Configure the PostgreSQL Database

Before your frontend application can interact with Supabase, the database must be properly configured. Supabase uses PostgreSQL as its core database engine and provides features such as authentication, row-level security (RLS), and API keys to control access. Understanding these essentials ensures your application can securely read, write, and manage data:

  • Tables and Columns: The frontend interacts with tables in the database. Each project defines its own schema (tables, columns, and data types) depending on the functionality required. At minimum, define a table structure that matches your app’s data model.

  • Row-Level Security (RLS): Supabase enforces RLS by default to ensure production safety. RLS defines which users can access or modify specific rows. Enabling and configuring RLS protects sensitive data while allowing authorized operations.

  • API Keys and Roles:

    • ANON_KEY: A public key for client-side operations such as fetching data or inserting new records with restricted privileges.
    • SERVICE_ROLE_KEY: A secret key for server-side operations requiring elevated privileges, such as admin tasks.

    The combination of RLS and properly configured API keys ensures your frontend can securely access the database.

  • Extensions: PostgreSQL supports optional packages called extensions that add extra functionality to your database. Extensions can provide new data types, functions, operators, or utilities that simplify common tasks. Supabase is pre-configured with over 50 extensions, including common PostgreSQL extensions such as UUID generation (uuid-ossp) and cryptographic functions (pgcrypto). To see the full list of supported extensions, visit the Supabase Postgres Extensions Documentation.

Using Docker CLI and SQL Commands

Follow this method to use the Docker command-line interface to access the running Supabase PostgreSQL container and execute SQL commands directly inside the container for custom database configurations.

  1. To run Docker commands without sudo, add your user to the docker group.

    console
    $ sudo usermod -aG docker $USER
    
  2. Apply the group changes to your current session so they take effect immediately.

    console
    $ newgrp docker
    
  3. List running containers to identify the Supabase database container.

    console
    $ docker ps
    

    Look for the container name or image associated with Supabase’s database, typically something like supabase-db.

  4. Open an interactive shell inside the PostgreSQL container. Replace supabase-db with the actual container name if different.

    console
    $ docker exec -it supabase-db bash
    
  5. Connect to PostgreSQL using the default postgres user

    console
    root@<container_id>:/# psql -U postgres
    

    PostgreSQL automatically connects you to the default database named postgres.

  6. Create a table that matches your application's data model.

    sql
    postgres=> CREATE TABLE notes (                                                  
        id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
        title TEXT NOT NULL,
        content TEXT,                                                     
        created_at TIMESTAMPTZ DEFAULT NOW()
    );
    
  7. Supabase enforces RLS by default in production. Enable RLS on a table before defining policies.

    sql
    postgres=> ALTER TABLE notes ENABLE ROW LEVEL SECURITY;
    
  8. Policies control which roles can read or modify specific rows in a table. Define them according to the access requirements of your application to ensure secure and appropriate data access.

    sql
    postgres=> CREATE POLICY "Allow select for all" ON notes FOR SELECT USING (true);
    postgres=> CREATE POLICY "Allow insert for all" ON notes FOR INSERT WITH CHECK (true);
    postgres=> CREATE POLICY "Allow update for all" ON notes FOR UPDATE USING (true);
    postgres=> CREATE POLICY "Allow delete for all" ON notes FOR DELETE USING (true);
    
  9. Exit the PostgreSQL shell.

    sql
    postgres=> \q
    
  10. Exit the container shell.

    console
    root@<container_id>:/# exit
    

Connect the Frontend Application

Next, deploy the frontend application and connect it to the Supabase backend. The example used here is a full-stack notes application built with Next.js and Supabase. The app lets you create, read, update, and delete notes while handling authentication and real-time updates via Supabase.

  1. Clone the application repository. Replace the repository URL with your own GitHub repository.

    console
    $ git clone https://github.com/SanskritiHarmukh/supabase-notes-app.git
    
  2. Navigate into the project directory.

    console
    $ cd supabase-notes-app
    
  3. Examine the project structure:

    console
    $ ls -la
    

    Key files and directories include:

    • src/ - Contains the application source code and components, including the lib/ directory where the Supabase client is initialized.
    • package.json - Defines project dependencies and build scripts.
    • Dockerfile - Specifies container configuration for deployment.
    • docker-compose.yml - Defines service orchestration and container setup.
    • .env.example - Provides a template for required environment variables.
  4. Create your environment configuration file.

    console
    $ cp .env.example .env
    
  5. Update the .env file with Supabase credentials and domain:

    console
    $ nano .env
    
  6. Configure the required variables.

    NEXT_PUBLIC_SUPABASE_URL=http://yourdomain.com/supabase/
    NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key_here
    • NEXT_PUBLIC_SUPABASE_URL: This URL points to your domain with the /supabase/ path, which Nginx proxies to port 8000. The NEXT_PUBLIC_ prefix makes this variable available in the browser for client-side API calls.

      [!Note] For now, use http in the NEXT_PUBLIC_SUPABASE_URL.

    • NEXT_PUBLIC_SUPABASE_ANON_KEY: The anonymous key you generated earlier, used for public API operations with Row-Level Security policies applied.

    Warning
    The SUPABASE_SERVICE_ROLE_KEY generated earlier, bypasses Row-Level Security and should only be used in server-side code for administrative operations. Never expose this key in client-side code or commit it to public repositories.

Deploy the Application

The Vultr Supabase Marketplace instance comes pre-installed with Node.js, npm, and Docker, giving you flexible deployment options. You can run the application directly with Node.js/npm or use Docker for consistent containerized deployment.

Using Node.js (npm)

Running the application directly via Node.js is ideal for development, testing, or lightweight production deployments where Docker is not required.

  1. Install Node.js packages defined in package.json.

    console
    $ npm install
    
  2. Compile the Next.js application into an optimized production build.

    console
    $ npm run build
    
  3. Launch the application on the configured port (default is 3000).

    console
    $ npm start
    
    Note
    Running npm start directly occupies your terminal session. The process stops if the SSH connection is closed. To prevent this, run the process in the background using nohup npm start &.

Using Docker

Using Docker is recommended when you want a self-contained, portable deployment that works consistently across environments. Docker isolates the application, its dependencies, and its runtime, which simplifies scaling, updates, and migration.

  1. Review the Docker configuration

    console
    $ cat Dockerfile
    

    The Dockerfile defines how to build your application container, including dependency installation, application building, and runtime configuration.

  2. Examine the Docker Compose configuration:

    console
    $ cat docker-compose.yml
    

    This file orchestrates the container deployment, including port mapping, environment variable injection, and networking configuration.

  3. Build and deploy the application.

    console
    $ docker compose up -d
    

    This builds a Docker image containing your application, creates and starts a container in detached mode (background), maps port 3000 from the container to the host, injects environment variables from the .env file, and configures restart policies for reliability.

  4. Verify the deployment.

    console
    $ docker compose ps
    

    The output should show your application container running with status "Up" and port mapping 0.0.0.0:3000->3000/tcp.

  5. Inspect the container logs to view startup messages and confirm that the application is running correctly. Replace <service-name> with the name of the service defined in your docker-compose.yml file (e.g., notes-app).

    console
    $ docker compose logs -f <service-name>
    

Access the Application

After deploying the frontend application and configuring Nginx to proxy requests to your instance, the application becomes accessible through the configured domain. Open a web browser and navigate to,

http://yourdomain.com

Notes App

Note
At this stage, SSL/TLS is not yet configured. Your browser may display a warning such as “Connection is not secure” or “Continue to site (unsafe)”. This is expected for testing purposes. You can safely proceed to the site, but enabling HTTPS is recommended for production.

Once the page loads, you should see the Notes App interface. This interface allows you to create, view, edit, and delete notes. All interactions are backed by Supabase, with data stored in your PostgreSQL database and row-level security (RLS) enforced.

To verify the application's functionality:

  1. Enter a Title and Content for a new note, then click Add Note.

    Accessing the Application

  2. The newly added notes appear in the list and are stored in the database. From here, you can Edit or Delete any existing note. Editing updates the database in real time, and deletion removes the corresponding row.

    CRUD on Notes Application

  3. Open your PostgreSQL database and check the notes table. Any changes made through the frontend (adding, editing, or deleting notes) should be reflected in the table immediately. This confirms that the frontend is correctly connected to Supabase and RLS policies are applied as expected.

    Row Creation in notes Table

Open the application in multiple tabs or devices. Adding or editing notes in one tab should update the view in other tabs instantly, demonstrating Supabase’s real-time subscription functionality.

Optional: Enable HTTPS with SSL/TLS Certificates

HTTPS encrypts data transmission between users and your server, protects against man-in-the-middle attacks, and is required by modern browsers for many features like geolocation and camera access. Plus, search engines favor HTTPS sites, and users expect the security assurance provided by the padlock icon in their browser.

This section uses Let's Encrypt, a free, automated certificate authority that provides SSL/TLS certificates trusted by all major browsers. The process is automated using Certbot, which integrates directly with Nginx to obtain and configure certificates.

  1. Install Certbot with required system dependencies.

    console
    $ sudo apt install ca-certificates curl gnupg lsb-release certbot python3-certbot-nginx -y
    
    • ca-certificates: Contains trusted certificate authorities needed to verify Let's Encrypt's SSL certificates.
    • curl: Used by Certbot to communicate with Let's Encrypt servers.
    • gnupg: Provides cryptographic functions for certificate validation.
    • lsb-release: System information utilities used by Certbot.
    • certbot: The core certificate management tool.
    • python3-certbot-nginx: Plugin that automatically configures Nginx for SSL.
  2. Generate SSL certificates for your domain. Replace yourdomain.com with your actual domain and admin@example.com with your email address.

    console
    $ sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com --email admin@example.com --agree-tos --non-interactive
    
  3. Verify the certificate installation.

    console
    $ sudo certbot certificates
    
  4. Let's Encrypt certificates expire after 90 days. To simulate and test certificate renewal, run:

    console
    $ sudo certbot renew --dry-run
    

    A successful dry run confirms that automatic renewal works when certificates approach expiration.

  5. Now that HTTPS is enabled, update your application’s environment variables to use the https protocol.

    1. If you are not already in your application directory, navigate to it first:

      console
      $ cd supabase-notes-app
      
    2. Open the environment configuration file for editing.

      console
      $ nano .env
      
    3. Change the NEXT_PUBLIC_SUPABASE_URL to use HTTPS.

      NEXT_PUBLIC_SUPABASE_URL=https://yourdomain.com/supabase/
  6. Restart the application to apply changes.

    • If you deployed the application directly using npm without containerization, you need to restart the Node.js process.

      1. Identify the running Next.js process.

        console
        $ ss -ltnp | grep 3000
        
      2. Stop the current process.

        console
        $ kill [PID_from_previous_step]
        
      3. Rebuild the application to apply the updated .env values.

        console
        $ npm run build
        
      4. Restart the application.

        console
        $ npm start
        
    • If you deployed using Docker Compose, the application needs to be restarted to pick up the updated environment variables from the .env file.

      1. Stop the running container.

        console
        $ docker compose down
        
      2. Rebuild the image with updated configuration and start the container.

        console
        $ docker compose up -d --build
        
  7. Access the application again by visiting,

    https://yourdomain.com

    Accessing Application Over HTTPS

    Your browser should now display a padlock icon, confirming that the connection is secure and the SSL certificate is active. All data transmitted between users and your server is encrypted. The application continues to function as before, now with production-grade security.

Conclusion

You have successfully deployed a Next.js application with Vultr’s Supabase Marketplace Application, configured PostgreSQL with row-level security, and secured the deployment with Nginx and SSL certificates. Your Supabase instance now delivers a complete backend with database, authentication, and API services accessible through your custom domain. By using the Vultr Supabase Marketplace, you reduce setup effort and can focus on building application features with Supabase’s full capabilities.

Comments