Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/KevxxAlva/tiktok-bot-downloader/llms.txt

Use this file to discover all available pages before exploring further.

Self-hosting TikTokSaver gives you full control over the deployment environment, custom domain, and resource limits. The application consists of two independent pieces: the Express API in api/ and the compiled React frontend in client/dist/. You run the API as a long-lived background process and point a reverse proxy — Nginx or Caddy — at both the static files and the API, so everything is served from a single origin.

Prerequisites

Before you begin, make sure your server has the following available:
  • Bun v1.0+ (recommended) or Node.js 18+ — the API is a CommonJS Express app that runs on either runtime.
  • A Linux server or VPS with a public IP address (Ubuntu 22.04 LTS or Debian 12 are well tested).
  • git installed and network access to clone the repository.
  • A reverse proxy — Nginx or Caddy — installed and able to bind to ports 80 and 443.

Setup

1

Clone the repository

SSH into your server and clone the project into a directory of your choice:
git clone https://github.com/KevxxAlva/tiktok-bot-downloader.git
cd tiktok-bot-downloader
2

Install dependencies

The monorepo provides dedicated root-level scripts to install dependencies for each workspace independently. Run both before building:
# Install API dependencies
bun run install-server

# Install frontend dependencies
bun run install-client
These scripts are defined in the root package.json:
{
  "scripts": {
    "install-server": "cd api && bun install",
    "install-client": "cd client && bun install"
  }
}
3

Build the frontend

Compile the Vite + React application into static assets. The output lands in client/dist/.
bun run build-client
This runs cd client && bun run build, which executes bun tsc && bun vite build inside the client/ workspace. After it completes, verify the output exists:
ls client/dist
# index.html  assets/  ...
4

Start the API server

The API is started with the start script inside api/:
cd api && bun start
This runs bun index.js. By default the server listens on port 3000. Override the port by setting the PORT environment variable before starting:
PORT=8080 bun start
The port binding comes directly from api/index.js:
const PORT = process.env.PORT || 3000;

// For local development
if (require.main === module) {
  app.listen(PORT, () => {
    console.log(`🚀 Server running on http://localhost:${PORT}`);
  });
}
Confirm the server is running:
curl http://localhost:3000/api/download?url=https://www.tiktok.com/@test/video/1
5

Configure a reverse proxy

Serve the compiled frontend from client/dist/ as the document root and proxy all /api/* requests to the running Express server. Choose the proxy that fits your setup.

Nginx

Create a new site configuration at /etc/nginx/sites-available/tiktoksaver:
server {
    listen 80;
    server_name yourdomain.com;

    # Serve the Vite build
    root /path/to/tiktok-bot-downloader/client/dist;
    index index.html;

    # Proxy API requests to the Express server
    location /api/ {
        proxy_pass http://localhost:3000/api/;
        proxy_http_version 1.1;
        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;
    }

    # Fall back to index.html for client-side routing
    location / {
        try_files $uri $uri/ /index.html;
    }
}
Enable the site and reload Nginx:
sudo ln -s /etc/nginx/sites-available/tiktoksaver /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Prefer automatic HTTPS without manual certificate renewal? Caddy handles TLS via Let’s Encrypt out of the box. A minimal Caddyfile for TikTokSaver looks like this:
yourdomain.com {
    root * /path/to/tiktok-bot-downloader/client/dist
    file_server

    reverse_proxy /api/* localhost:3000

    try_files {path} /index.html
}
Run caddy start (or caddy run) and Caddy provisions and renews your TLS certificate automatically — no Certbot required.

Running the API as a background process

The bun start command in the foreground will stop when you close your SSH session. Use a process manager to keep the API running persistently.

pm2

# Install pm2 globally
bun add -g pm2

# Start the API with pm2
cd /path/to/tiktok-bot-downloader
pm2 start api/index.js --name tiktoksaver-api --interpreter bun

# Save the process list and enable startup on reboot
pm2 save
pm2 startup
Useful pm2 commands:
pm2 status          # View all managed processes
pm2 logs tiktoksaver-api   # Tail live logs
pm2 restart tiktoksaver-api
pm2 stop tiktoksaver-api

systemd

Create a unit file at /etc/systemd/system/tiktoksaver.service:
[Unit]
Description=TikTokSaver API
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/path/to/tiktok-bot-downloader/api
ExecStart=/usr/local/bin/bun index.js
Restart=on-failure
Environment=PORT=3000

[Install]
WantedBy=multi-user.target
Enable and start the service:
sudo systemctl daemon-reload
sudo systemctl enable tiktoksaver
sudo systemctl start tiktoksaver
sudo systemctl status tiktoksaver

CORS considerations

In self-hosted mode, if the frontend and the API are served from different origins (for example, the static files are on a CDN subdomain while the API is on a separate host), you must update the CORS configuration in api/index.js. By default, app.use(cors()) allows all origins, which is permissive but acceptable for personal deployments. For stricter setups, pass an explicit origin option:
app.use(cors({ origin: 'https://yourdomain.com' }));
When the reverse proxy setup described above is used (Nginx or Caddy on a single domain), both the frontend and API share the same origin and no changes to the CORS configuration are needed.

pm2 documentation

Full reference for managing Node.js / Bun processes with pm2, including cluster mode and log rotation.

Caddy documentation

Learn how Caddy’s automatic HTTPS and reverse proxy directives work for production deployments.

Build docs developers (and LLMs) love