Nile: Serverless Postgres for modern SaaS
thenile.dev
thenile.dev
The tenant capabilities are nice to have, but even when building a tenant-focused SaaS not really a big draw. The examples shown even come down to "you can achieve 80% of this by just adding a tenant column everywhere". When comparing this to e.g. Supabase, their autogenerated REST/GraphQL APIs solve a lot more significant pain points, especially when starting out.
That's usually something that I'd outsource as much as possible to Auth0 (yikes, considering the last days), as building out the UIs and state handling for all user auth and management flows is a huge chore. I think recently Clerk has set a new standard on how easy that can be. I'd be surprised if a database product could compete in a meaningful way in that area.
I'm looking forward to see what Nile evolves to! (IIRC Supabase also didn't look that enticing when it first appeared on the map). In general I'm really happy to see databases as such an active space of innovation again.
Nile user permissions are optional. You can decide to use any third-party provider for it. We are in the design phase for permissions at this point. We would love to get your feedback to understand more about your concerns https://github.com/orgs/niledatabase/discussions/159
https://supabase.com/docs/guides/getting-started/architectur...
This is why we don’t run a fork of Postgres, and we lean heavily into extensions rather than customizations. As the docs mention: this forces us compete on experience.
(Speaking here to your point about supabase, not to detract from Nile, which looks very cool)
As this does regional accesses of shared and modifiable data, does Nile implement or guarantee snapshot consistency for cross-shard queries? Citus doesn't have this yet, iirc.
From the docs, I assume Nile does something like the following:
- "smart partitioning" of tables on tenant_id when tenant_id is detected on table creation. These partitions are either region-local when the tenant is located in that region, or foreign tables. (Alternatively, non-local tenant data is replicated with e.g. logical replication/LR, or triggers)
- global tables are replicated across all regions, again presumably with LR
- RLS on the smart partitioned tables, which applies the tenant filter when nile.tenant_id is set.
Do you have any docs where I can read up on this in more detail?
All in all this looks like a real neat project. Congrats on launching.
Congrats on the launch. I would love to try out Nile in the future!
Specifically, Nile lets you do the following - Create a database and start querying it. Nile takes care of adding capacity to tenants as workload increases. - Pay for what you use. You will only pay for what you use. We have plans for pricing where you can pay based on usage per tenant. This will ensure your business value is aligned with the cost of your database. For example, a customer on your free tier may not be an active user and you would not pay for them in Nile. - We have built multitenancy into Postgres and a gateway layer that routes. This helps us to scale to zero with instant availability when you want to scale back up.
- You can create even a million tenants if you have that many customers. - We have built connection pooling into Nile. This helps to provide limitless connections as you grow
I'm still trying to understand the scaling story better. When we say serverless it mentions automatically scaling when it detects some sense of resource pressure. If I have a "hot tenant-database", does that mean this shard will be scaled automatically without impact to existing queries? Or would there be some "blip". I suppose it's unavoidable in edge cases but curious about the regular ones as well.
It's an incredibly cool CX you have here with the automated query routing/tenancy story though, looking forward to what happens in this space.
The first meaning I was familiar with was the idea that there was no long-running process operating a service. This was like a lambda, executing upon a trigger and then stopping.
The second meaning I learned about more recently was the idea of "serverless" being an abstraction layer, where you don't think about what server the service is deployed on (specifically, you get to stop caring about the resources allocated to that server), even though it is deployed as a continuous, long-running process.
I was also only aware of your first definition of serverless.
AWS Lambda lets you think about "invocations" and not CPU and you trust that the CPU will show up when needed. At Nile we hope to let you focus on "queries per sec" and even "queries per second per tenant" while we scale the infrastructure as needed for you.
Incidentally, our product has a dashboard that shows you exactly these metrics :)
AWS Lambda has a default concurrency limit of 1k and each function has a configurable memory limit.
Sure, those limits allow for a lot more elasticity and flexible pricing but the limitations haven't disappeared.
Also, there are now some 'serverless' hosting providers that do fixed monthly pricing (fermyon.com/pricing). Isn't this taking the best part of serverless ad absurdum or is there something I'm missing?
Lastly, I hope I don't sound argumentative. I work for a company that has the described problem of multi-tenant SaaS and I love Postgres, so I am fully supportive of your mission!
I absolutely love PlanetScale but having 1 database per client means paying for 1 plan per client. I'm B2B so I have way fewer "clients" but my load is incredibly spiky for each client (75% of the time they have near-0 usage, 20% of the time they have a tiny amount of usage, and the 5% they use a bunch). As it stands right now their heavy load never overlaps so I've been mulling over a move to an RDS cluster where I can run multiple databases on the same hardware.
I'm not super interested in migrating to Postgres (though I think my app would move relatively easily, I don't use anything super MySQL-specific, it's just what I know) but I'll be watching this project as it moves forward. I'd really like to not manage my own DB, it's not where I'm strong and I'd rather focus on other things.
I guess this gets a pass since it's not a "Show HN" but that junk drives me crazy
Nice surprise to see our launch here. Happy to answer any questions...
Modern SaaS applications are multi-tenant. We’re the first database that virtualizes tenants into the database. A tenant is primarily a company, an organization, or a workspace in your product that contains a group of users. This enables seamless tenant isolation, per-tenant backups, and placement on multi-tenant or dedicated infrastructure, anywhere on the planet. You can do all this with the experience of a single Postgres! You don’t have to manage multiple databases, build complex permissions for isolation, or write buggy scripts to read specific tenant data from backups. On top of the tenant model, we provide opt-in user management capabilities, customer-specific vector embeddings, and instant tenant admin dashboards.
You manage schemas like how you manage with standard Postgres. Your favorite tooling should work. When you make the schema change, Nile pushes the schema to all the tenants. For the user, it is like interfacing with a single Postgres instance.
We have built a Rust based layer internally to manage tenants, route them, shard them if needed and have multiple tenants talk to each other. Happy to chat more. My email is ram@thenile.dev
Also, how is Nile different from creating a database (or several databases) for each tenant within a single Postgres cluster?
Nile is different from DB per tenant in a single PG cluster in that it is much lower overhead per tenant. Since we offer a service, it will translate to lower cost.
The DB-per-tenant gets painful after the first few 100 tenants, but some SaaS have thousands of tenants, each with very low activity. Our model makes inactive tenants nearly free.
In addition, we also provide the "unified view", which lets you query all your tenants as if they were in the same DB. So you can connect as an admin and "select * from my_table" will give you the results for all tenants. Can be pretty handy.
What are you thinking will happen when a SaaS grows like Salesforce, with a very large number of tenants, where some of the tenants have very large databases? What will Nile do when the database just can't fit in a single Postgres cluster?
Edit: Never mind, I see from your web site that moving tenants between databases is one of the main things you try to address.
This is true in a sense “most modern SaaS apps have at least two unrelated users or user groups” (forgive me please if I’m wrong here, but that’s how I loosely understand “multi-tenant”), but it’s not exactly clear why this is important.
I strongly suspect that the majority of modern SaaSes don’t do any database-level tenant isolation or per-tenant backups. The smaller the company the less it’s likely. Row-level security is a very rare sight to behold, even in regulated areas (certifications like HIPAA compliance don’t really require this). The best they do if they’re really global (I’m sure most are only global in “we accept users from anywhere”, but not so much under the hood - even though cloudy sales pitches love to paint a different picture, most SaaSes I’ve seen were one or two-region deployments) is try to move/shard data closer to the the user’s region.
I think one concrete example (as simple as possible) would help a lot.
I'm not sure that I would generalize that to all modern SaaS applications though.
1. GitHub helps a group of developers manage and deploy their code. Each Github organization is a tenant, and the developers within those organizations are users. 2. Salesforce or Hubspot helps sales reps manage their leads. Each company is a tenant, and the users are the sales reps. 3. Ring is a home security company that provides alarm services to different households. Each household is a tenant, and people living in a house using their service are users. 4. Toast is a platform to help build software for restaurants. The restaurants are tenants, and the employees of the restaurants are the users.
Nope, Github users exist outside of organizations in a global namespace, and can be invited to be members of multiple organizations.
Tenants are your customers. It is normal for SaaS to have either a schema per tenant or one schema and tenant_id column in most tables. Nile provides you with both experiences at the same time.
For most of my apps, I have very little idea on their region aside from their login IP origin, and we get a high level of multi-region travels (like conference organizers). I'd encourage you to transform the notion of a tenant transfers from a 'major decision' to 'assume users will/should switch to closest tenant login'. It looks like your platform already allows this move-tenant-on-login approach, but I'd love to see more docs and more tests around that use-case.
update tenants set deployment_mode = 'shared' where name = 'customer 2';
Does that mean some downtime or broken connections for that tenant while data is shuffled around?The architecture includes proxy layer that handles routing and maintains the connections, so we believe broken connections are avoidable.
If they travel a lot, you may want the data in one location (although it still depends, moving data is often faster than flying to a different region).
Tenant placement is especially great if their location has data residency requirements. We discovered that companies with many EU customers need this.
With Flask it is fairly straightforward - you add the tenant configuration to the DB connection when you pick it up to serve a request. The Java and NodeJS examples do something similar if you need to see the idea.
This is the approach we took and I think it helps with IT review docs.
Sounds promising, but by "a dedicated Postgres." do you mean a different server or just a different db on the same server? I'm trying to get at how the cost would change between the two solutions. Right now we can run N databases on the same Postgres server for roughly the same cost as running a single multitenant db. Switching to "server per customer" seems like it would be cost prohibitive.
If you are building multi tenant saas you should totally look at them!!
We’re the first database that virtualizes tenants into the database. This enables seamless tenant isolation, per-tenant backups, and placement on multi-tenant or dedicated infrastructure anywhere on the planet. You can do all this with the experience of a single Postgres.
Here are some examples that can help you to understand the type of use cases. Most applications are multi-tenant and this is what we help with.
1. GitHub helps a group of developers manage and deploy their code. Each Github organization is a tenant, and the developers within those organizations are users. 2. Salesforce or Hubspot helps sales reps manage their leads. Each company is a tenant, and the users are the sales reps. 3. Ring is a home security company that provides alarm services to different households. Each household is a tenant, and people living in a house using their service are users. 4. Toast is a platform to help build software for restaurants. The restaurants are tenants, and the employees of the restaurants are the users.
Happy to chat more. My email is ram@thenile.dev
Both approaches have pros and cons. This is exactly what Nile is solving. We want users to not worry about all the operational complexity of picking a choice. You can choose any approach in Nile.
In Nile, the tenant DB is a virtual concept. You can choose to place it on a multitenant DB along with all the other tenants or choose to place it on a dedicated DB. Nile provides a single experience irrespective of your choice and takes care of all the operational complexity. We also go one step further and also let you place a tenant in any location worldwide but still have one Postgres experience.
You can read more about it here https://www.thenile.dev/docs/tenant-management https://www.thenile.dev/docs/tenant-placement
Would love the feedback
I guess that's why this was created though. It's definitely an intriguing tool.
I did shift in past from single database to sharded via citus. The actual migration was smooth, but 6+ months of work went into ensuring compatibility.
There's ways to nearly guarantee compatibility without actually using a sharded approach, like enforcing all tables have a uniform shard key.
Curious if the sharding strategy that you were shifting to was company-based, as is implemented in this case?
There's a few different approaches and deciding which to use can be tough. When I was adapting a formerly single tenant Django application to a multi-tenant solution, I went for a "db-per-customer" (with many databases on a single db server) for a few main reasons:
1) Allows you to backup and restore databases on a per customer basis.
2) Helps with IT reviews if you can say "your data exists in its own DB and is not co-mingled with other customers data"
3) Implementation seemed like it was going to be simpler, especially for adapting an existing single tenant application to a multi-tenant one.
4) Makes it easy to migrate existing single tenant-db's to the multi-tenant app.
The main drawback for us I would say is that it makes managing database migrations/schema changes a bit more challenging since you now need to update N db's every time there's a schema change. This was not super difficult though.
Overall I am very happy we decided to go with a multi-db multi-tenancy solution.
Additional benefits: easy to support customer-managed encryption keys and can give their BI people direct access.
> brittle permission logic at the application level or complex, hard-to-debug, row-level security policies in databases like Postgres
Separating them into separate databases has its own problems with connection pooling, resource management, and query speed. It's also a pain when you have to combine the data. Then you go down the rabbit hole of ETL and SaaS products with a "scratch" interface that promises to speed things up through (yet another) customer DSL and new naming convention for everything you already know.
A better option is separating tenants by schema. Think about how you switch between schemas in Postgres. Queries look something like this:
> set search_path to public, tenantA; > select * from somewhere as smw inner join elsewhere lsw on smw.id = lsw.somewhere_id;
The somewhere table is in the public schema, elsewhere is in the tenantA schema.
In Rails, there are a couple gems that come to mind for multi-tenancy. acts_as_tenant works exactly like this, by swapping the tenant schema in ActiveRecord (the Rails ORM layer). The ros-apartment gem is another (formerly just 'apartment'). I ran across another gem called 'roomer' too but it is outdated. It works the same way though. You could even roll your own if you insert the logic at the right layer of whatever stack you're using.
Set the tenant at the request level and the ORM auto switches the schema for you. Match a subdomain to the tenant and away you go. You got a SaaS application. edit: And, it'll pass a security audit because of the separation. And you can query across LOB tenant data in a BI front end if you can live without real-time data and build some materialized views; if Redshift and Snowflake are just too expensive.
Using schema separation you get the best of both worlds; smaller data in tenant specific tables with the ability to cross tenant query because they're in the same database. And yes, I'm well aware foreign servers are a thing in Postgres but with sufficiently large data you have to spend more time tuning them for fetch size and indexes to get decent performance. Forget the fact that sometimes you have to implement dynamic SQL using SQL which is a nightmare of repetitious quotation marks with any complex data structure. Foreign servers amplify the escaped quotes. My stomach turns remembering having to write that code...
Is there a npm package for something like this? React/node is not my daily driver.
We build our JS SDK by wrapping Knex (nice query builder, not quite an ORM) and injecting our logic to the connection process. You could do the same.
The issue is that there are at least 5 popular JS ORMs, so we don't get the same solve-it-once that we get in the Rails ecosystem.
I will explain what Nile tries to do. Happy to follow up.
The most foundational element in SaaS is a tenant. It makes a lot of sense to build this core concept into the database. Imagine having a lot of virtual tenant databases that can be co-located on one physical Postgres (multi-tenant) for better cost, and some of them can be placed on a dedicated database for better isolation. The virtual tenant DBs can be located anywhere on the planet for low latency or compliance. The client can route to the right tenant seamlessly without routing logic in the application.
Isolating tenants into their own virtual DBs is great, but you will also want to be able to share data across tenants where it makes sense. Backups should be available for each tenant, and it should be possible to restore them instantaneously. Schema changes should be applied seamlessly across all the tenant DBs, and it should also be possible to do staged rollouts for different tenant tiers. While supporting all this, all the standard SQL capabilities should work across the tenants for admin operations. All the standard Postgres tooling should work. You want the experience of a single Postgres.
This is what Nile tries to achieve. A model that gives you fine-grained control over your tenants. You create, scale, locate, pay all by tenants.
I use listen/notify and triggers as some sort of pubsub.
W.r.t replicating data across tenants, could you give an example use case for it?
LICENSE
Apache-2.0
https://github.com/niledatabase/niledatabase/blob/main/LICEN...
Edited:
This is the docs repo.