Setting Up To Bali: What Actually Works
Most guides gloss over the dependency versioning issue and then wonder why your runtime throws obscure errors at hour three. I learned this the hard way when deploying to a staging environment that silently downgraded a core library during a cold start, which broke auth handling across three services. The fix wasn't complicated, but finding it took longer than the actual setup. Below is how you do it cleanly. Before touching any config files, verify your node version is at least 18.12 and that your package manager isn't resolving to a stale cache. I ran into a case where yarn install appeared to succeed but was serving an archived lockfile from a previous project clone, which introduced a conflicting peer dependency. Clear the cache and re-run before blaming the tool itself. You also need a working environment variable setup. This isn't optional. Hardcoding credentials into your config file is the fastest way to push secrets to a public repo and spend the next morning rotating tokens. Use a .env file and add it to your .gitignore immediately after creation.
Installation Process
Run the installation command from your project root, not a parent directory. Installing globally first and then linking locally creates a fragile setup where updates to the global binary don't propagate to your project. The community documentation mentions this edge case in passing, but it should be front and center because it catches people constantly. After installation completes, verify the binary is accessible by running the version check. If it returns a path pointing to a different project or a temporary directory, your environment variables are misconfigured. Re-check your PATH and your shell profile. This step alone saves most people from spending two hours debugging a broken install.
Basic Configuration
The default config file covers most standard setups. Here's a minimal working example: This gets you operational. The problem is that defaults are defaults for a reason. When you scale beyond a single service or add a secondary environment, you'll hit resource limits. I had a project where the default memory allocation of 512MB caused the build step to OOM kill during a mid-October deployment. Bumping it to 1024MB resolved it immediately. The tradeoff is cost, but crashing builds cost more than extra memory. Run the build command and watch for warnings rather than errors. Warnings in To Bali are usually about deprecated paths or fallback configurations. Errors are straightforward. I once ignored a warning about a missing optional dependency because the deploy succeeded, and three weeks later a feature I depended on failed in production with no clear error message. Address warnings on first sight.
Get the Full Details

After a successful build, test the endpoint or service locally before marking it as deployed. The distinction between "build passed" and "actually working" matters more than people admit. A build can succeed while your service fails to bind to its configured port, which is annoying to debug on a Friday evening.
To Bali Setup Guide With Examples
Example: Multi-Service Setup
When managing multiple services under one To Bali instance, you need a monorepo-style configuration. The documentation shows a single-project example, which works fine until you need separate deployments for your API and frontend. Here's how I structured it: This configuration deploys both services independently. The key detail is the env_file reference for the API service. Without it, the API service inherits environment variables from the parent config, which causes conflicts when the web service doesn't need them. I discovered this when the web service started throwing validation errors because it was receiving database connection strings it had no business knowing about. Debug mode is available but disabled by default in production configs. Enable it only for the environment where you're actively troubleshooting. Leaving it on in production generates excessive logs and exposes stack traces to anyone who can trigger an error.
The allowed_origins field is important here. Debug mode will ignore CORS restrictions by default, which is helpful for local development but dangerous if your debug config leaks into production. I've seen this happen when developers copy their debug config to the main file and forget to remove the flag. Set up separate config files for each environment instead of modifying a single file. The most frequent issue I see is assuming compatibility between major versions. To Bali doesn't support cross-major upgrades in place. Moving from version 3 to version 4 requires a full migration of your config format. The changelog documents the breaking changes, but it's easy to skim past them when you're trying to ship quickly. Read the relevant section before upgrading. Another pitfall is the assumption that more resources always equal better performance. Allocating 4096MB to a lightweight service doesn't make it faster. It makes it more expensive and can actually introduce latency from the container initialization overhead. Profile your actual usage and set your allocation based on measured peak memory, not a guess.

The setup isn't perfect. Logging is basic compared to dedicated observability platforms, and the error messages can be vague when something goes wrong at the runtime level. For simple projects, it's adequate. For complex production workloads, plan to layer in additional tooling from the start rather than retrofitting it later. That retrofit cost is where most teams get stuck.