For the complete documentation index, see llms.txt. This page is also available as Markdown.

Manual Deployment

Deploy Hexabot directly with Node.js, PostgreSQL, NGINX, and HTTPS.

Choose this method when you want to run Hexabot directly on the host with Node.js.

Use this path when you want full control over the runtime and do not want Docker in production.

For shared deployment concepts, see How can I deploy my Hexabot project?.

Prerequisites

You need:

  • A Linux server with SSH access

  • Node.js >= 24.17.0

  • One package manager: npm, pnpm, yarn, or bun

  • PostgreSQL

  • NGINX

  • Certbot

  • A public domain that points to your server

Install the base packages first:

sudo apt-get update
sudo apt-get install -y nginx certbot python3-certbot-nginx postgresql postgresql-contrib
sudo systemctl enable --now nginx postgresql

Install Node.js before you run any Hexabot command.

If you install Node.js from NodeSource APT packages, the binaries are usually under /usr/bin.

If you install Node.js with nvm, use the absolute node path later in the systemd service.

Helpful setup guides:

Project setup

Install the CLI globally:

Create the dedicated service user before you clone the project.

This guide uses a ubuntu system user and the home directory /home/ubuntu .

Clone the project directly into its final path and install dependencies as hexabot:

If the repository is private, use a deploy key, a machine user, or HTTPS with a token.

If .env is missing, create it once:

hexabot env init copies .env.example to .env.

Use it only when .env does not exist yet.

If the project already contains a .env file generated by the Hexabot CLI, keep that file and do not rerun the command.

Use --force only when you intentionally want to replace the existing .env from .env.example.

It does not merge values.

Run these checks before you continue:

If you keep the project in your home directory instead, replace User, WorkingDirectory, and EnvironmentFile later with your own values.

Environment configuration

Generate strong secrets before you edit .env:

Edit /opt/hexabot/my-project/.env and use production values like these:

Variable
Example value
Notes

NODE_ENV

production

Use production for ongoing public deployment.

PORT

3000

Default Hexabot app port.

APP_DOMAIN

mychatbot.ai

Public domain name.

SSL_EMAIL

admin@mychatbot.ai

Email used by Certbot.

API_ORIGIN

https://mychatbot.ai/api

Public API base URL.

FRONTEND_BASE_URL

https://mychatbot.ai

Public app URL.

FRONTEND_ORIGIN

https://mychatbot.ai

Allowed browser origin.

HTTPS_ENABLED

true

Enable public HTTPS-aware behavior.

AUDIT_LOG_SERVICE_ENVIRONMENT

production

Use production on the public server.

DB_TYPE

postgres

Recommended production database.

DB_HOST

127.0.0.1

PostgreSQL host.

DB_PORT

5432

PostgreSQL port.

DB_USERNAME

hexabot

PostgreSQL user.

DB_PASSWORD

change-me

PostgreSQL password.

DB_NAME

hexabot

PostgreSQL database name.

DB_URL

postgresql://hexabot:change-me@127.0.0.1:5432/hexabot

Optional connection string.

DB_SCHEMA

public

Default PostgreSQL schema.

DB_SYNCHRONIZE

false

Keep this false for ongoing production.

DB_AUTO_MIGRATE

true

Enable on one primary API node.

API_IS_PRIMARY_NODE

true

Set true on the node that runs automatic migrations.

SESSION_SECRET

...

Use a long random value.

JWT_SECRET

...

Use a long random value.

PASSWORD_RESET_SECRET

...

Use a long random value.

CONFIRM_ACCOUNT_SECRET

...

Use a long random value.

SIGNED_URL_SECRET

...

Use a long random value.

UPLOAD_DIR

/var/lib/hexabot/uploads

Writable upload directory.

UPLOAD_MAX_SIZE_IN_BYTES

52428800

50 MB. Match the NGINX body size.

Optional SMTP variables:

Variable
Example value
Notes

EMAIL_SMTP_ENABLED

true

Enable transactional email.

EMAIL_SMTP_HOST

smtp.example.com

SMTP host.

EMAIL_SMTP_PORT

587

Common STARTTLS port.

EMAIL_SMTP_SECURE

false

Use true for implicit TLS on 465.

EMAIL_SMTP_USER

smtp-user

SMTP username or API key user.

EMAIL_SMTP_PASS

smtp-password

SMTP password or API key.

EMAIL_SMTP_FROM

Hexabot <noreply@mychatbot.ai>

Sender address.

Hexabot reads SMTP settings at startup.

Restart the app after any SMTP change.

PostgreSQL setup

PostgreSQL is the recommended database for production.

A typical PostgreSQL section in .env looks like this:

If PostgreSQL runs on the same server, create the Hexabot database and user:

Use the same database name, user, and password in .env.

You can also point Hexabot at a managed PostgreSQL service.

Native Ubuntu installs use the distro PostgreSQL version.

If you move data from a Docker PostgreSQL instance, use pg_dump and pg_restore.

Do not copy the raw data directory between versions.

Initial database bootstrap

Fresh PostgreSQL deployments need one private bootstrap before public traffic.

The first bootstrap may require development-style settings.

This is expected.

Database synchronization and seeding are disabled in production mode to reduce the risk of accidental data loss.

For the first bootstrap only, temporarily update .env with values like these:

If your project uses admin seed names, you can also set them here:

Then start the app in dev mode once:

Wait until the app finishes booting and the database is initialized.

Then verify that the admin user exists:

The output should include the seeded admin email.

Stop the bootstrap process after the admin user is created.

If you started it in the current shell, press Ctrl+C.

Switch back to production settings

After the first bootstrap, update .env again:

Keep DB_AUTO_MIGRATE=true and API_IS_PRIMARY_NODE=true on a single-server deployment.

If you already have a trusted initialized database, import that database instead of bootstrapping a fresh one.

Production build

This guide uses the compiled production bundle.

Build it from the deployed project directory before the first start:

This command creates the dist/ directory.

node dist/main requires dist/ to exist.

Some projects also expose npm run start:prod.

That command also requires dist/ to exist.

Run the build command:

  • before the first production start

  • after every code update

  • before you restart the systemd service

A typical update flow looks like this:

If Node.js comes from nvm, use the absolute npm path instead.

systemd process management

For production, keep Hexabot under systemd.

systemd restarts the app on failure and starts it again after a reboot.

Create the service file:

Add this configuration:

The [Install] section is required for systemctl enable hexabot.

That is what makes the service start automatically on reboot.

Enable and start the service:

Follow the service logs with:

Restart the service after each code update and rebuild:

NGINX reverse proxy

Create a dedicated site config:

And copy/paste the following nginx config:

This forwards both the admin UI and /api routes to the same Hexabot app.

Hexabot uses Socket.IO for real-time communication.

Keep the WebSocket upgrade headers in the location / block.

Do not add a rewrite rule for /api.

If you increase UPLOAD_MAX_SIZE_IN_BYTES, raise client_max_body_size to match.

Keep port 3000 reachable only from the local server.

Do not expose it publicly when NGINX is the reverse proxy.

Enable the site and remove the default site:

Validate and reload NGINX:

HTTPS setup

Request the certificate:

If you also use www, add it as another -d value.

Then verify renewal:

Finally, you can try and access https://mychatbot.ai in your browser.

Firewall setup

Allow only SSH, HTTP, and HTTPS from the public internet:

Do not open the Hexabot Node.js port, usually 3000, when NGINX is in front of the app.

Final verification checklist

Run these final checks:

Then confirm this checklist:

  • the production build completed successfully

  • .env uses NODE_ENV=production

  • .env uses DB_SYNCHRONIZE=false

  • .env uses AUDIT_LOG_SERVICE_ENVIRONMENT=production

  • SEED_ADMIN_PASSWORD has been removed or cleared

  • DB_HOST=127.0.0.1, not localhost

  • UPLOAD_DIR points to a writable native server path

  • the systemd service uses node dist/main, not npm run start

  • the systemd service has an [Install] section

  • systemctl enable hexabot has been run

  • NGINX includes WebSocket upgrade headers

  • the HTTPS certificate was issued successfully

  • UFW allows only SSH, HTTP, and HTTPS

  • port 3000 is not publicly exposed

  • you can sign in to https://mychatbot.ai

Troubleshooting

node dist/main fails with missing files

Run the build from the deployed project directory:

The production bundle must exist in dist/.

npm run build fails with a permissions error

Do not build as your login user inside /opt/hexabot/my-project.

That directory is owned by hexabot.

Build it as hexabot instead:

PostgreSQL login fails even though the password is correct

Check DB_HOST.

Use 127.0.0.1, not localhost.

The systemd service fails, but the app starts in your shell

The service often uses the wrong Node.js path.

Run which node and update ExecStart to that absolute path.

The browser loads the UI, but API requests fail

Do not rewrite /api.

Hexabot already serves the API under /api from the same app.

The first admin account was not created

The first admin is seeded only outside production mode.

Run the first private bootstrap with NODE_ENV=development, DB_SYNCHRONIZE=true, and SEED_ADMIN_* values set.

Then switch back to NODE_ENV=production and DB_SYNCHRONIZE=false.

Uploads fail through NGINX

Check both permissions and size limits.

The upload directory must be writable by the hexabot user.

Keep both limits aligned.

DNS or HTTPS does not work

Check DNS first:

Then recheck NGINX and Certbot:

Last updated

Was this helpful?