Technical
Moving My Laravel PostgreSQL Database to Supabase to Reduce Hosting Costs
I built ShipWithIan with Laravel, React, and PostgreSQL, and deployed it on Laravel Cloud. While the application hosting worked for my needs, I started questioning whether I needed to pay for a separate serverless PostgreSQL instance. Instead of migrating my entire application, I moved only the database to Supabase. Here's how I approached the migration, the problems I encountered, and what I learned about keeping infrastructure costs under control.
How I migrated ShipWithIan's PostgreSQL database from Laravel Cloud to Supabase, kept Laravel as my backend, and reduced my hosting costs without rebuilding my application.
Back to blogMoving My Laravel PostgreSQL Database to Supabase to Reduce Hosting Costs
One of the things I've been learning while building and deploying my own projects is that choosing the right technology isn't just about performance, scalability, or developer experience. It's also about understanding what you're paying for.
I recently migrated the PostgreSQL database of ShipWithIan, my personal portfolio and web development platform, from Laravel Cloud to Supabase.
The reason was simple: I wanted to reduce my recurring infrastructure costs without rebuilding my application.
Laravel Cloud's serverless PostgreSQL offering was convenient, but even with scale-to-zero, I found the database costs difficult to justify for my current workload.
Since my application was already built around PostgreSQL, I started looking for a way to keep my existing Laravel architecture while using a more affordable database hosting option.
That's when I decided to try Supabase.
This article covers why I made the move, how I migrated my database, the issues I encountered, and what my infrastructure looks like now.
1. My original architecture
ShipWithIan is more than a static portfolio website.
It's a Laravel application that I continuously develop as both my professional website and a platform for managing my own projects and client-related workflows.
My stack includes:
- Laravel 13 for the backend
- React, TypeScript, and Inertia.js for the frontend
- PostgreSQL for relational data
- Redis for caching and background processing
- Laravel Cloud for application hosting
The application includes a blog, project case studies, services, contact inquiries, an administrative dashboard, and internal client management workflows.
Initially, I hosted both the application and its PostgreSQL database on Laravel Cloud.
The architecture was straightforward:
Visitors
|
v
Laravel Cloud
|
+-- Laravel + React
|
+-- PostgreSQL
|
+-- Redis / Cache
|
+-- Object Storage
Keeping everything within one platform made deployment and resource management convenient.
However, as I continued using the platform, I started reviewing my infrastructure costs.
2. Why I decided to move the database
Laravel Cloud offers managed serverless PostgreSQL with automatic scaling capabilities.
For applications with changing workloads, this can be useful. You don't necessarily need to maintain a continuously running database server at a fixed capacity.
However, serverless doesn't automatically mean inexpensive.
Even with scale-to-zero, the total cost depends on how the database is used and the resources it consumes.
For my current application, I wasn't using enough database capacity to justify the recurring expense.
ShipWithIan primarily handles portfolio content, administrative operations, contact inquiries, and client-related records.
I didn't need to scale my database aggressively at this stage.
What I needed was:
- A managed PostgreSQL database
- Reliable connectivity from Laravel Cloud
- Enough storage for my existing application data
- A way to retain my current tables and migrations
- Lower infrastructure costs while my application is still growing
I wanted to continue using Laravel Cloud for application hosting, but I didn't necessarily need to host PostgreSQL there too.
The important realization was that my application hosting provider and database hosting provider did not need to be the same.
3. Why I chose Supabase
Supabase provides managed PostgreSQL along with other services, including authentication, object storage, realtime capabilities, and Edge Functions.
For this migration, however, I was interested in one thing: PostgreSQL.
I wasn't planning to replace Laravel authentication with Supabase Auth or move my application's business logic into Edge Functions.
Laravel already handles those responsibilities.
I simply wanted to use Supabase as an external PostgreSQL provider.
Supabase's Free plan also provided a useful starting point for evaluating the migration.
At the time of writing, its published Free plan includes:
| Resource | Included allowance |
|---|---|
| PostgreSQL database storage | 500 MB |
| Database compute | Shared CPU and 500 MB RAM |
| Egress | 5 GB per billing cycle |
| Cached egress | 5 GB per billing cycle |
These limits made it possible to evaluate whether the database could support my current workload without maintaining the separate Laravel Cloud database resource.
Of course, free infrastructure isn't unlimited infrastructure.
Database size, memory, compute capacity, and network usage still need to be monitored.
For my current application, I decided the trade-off was worth testing.
4. Migrating my database using pgAdmin 4
One advantage of using PostgreSQL on both platforms was that I didn't need to convert my database to another database engine.
I could use PostgreSQL's existing backup and restore tools.
For this migration, I used pgAdmin 4.
Step 1: Back up the Laravel Cloud database
I connected pgAdmin 4 to my existing Laravel Cloud PostgreSQL database using its database connection credentials.
Then I opened the database's Backup dialog and selected the Custom backup format.
The backup included my application's schema, table data, indexes, constraints, and other supported database objects.
I excluded source database ownership and privileges to help avoid role conflicts when restoring to Supabase.
I also kept a separate backup before changing any production database configuration.
Step 2: Connect pgAdmin to Supabase
Next, I created a new PostgreSQL server connection in pgAdmin using the credentials provided by Supabase.
One issue I encountered was that Supabase's default direct database connection used IPv6.
My local machine couldn't resolve the direct connection hostname, and Laravel initially returned:
SQLSTATE[08006] [7]
could not translate host name
Supabase provides a Session Pooler that supports IPv4 connectivity.
Instead of purchasing a dedicated IPv4 address, I switched to the Session Pooler connection details provided by my Supabase dashboard.
The connection used PostgreSQL port 5432 and the pooler's assigned hostname and database username.
After updating the connection details, I could connect successfully.
Step 3: Restore the database
With pgAdmin connected to Supabase, I restored the PostgreSQL backup into the destination database.
I imported my existing Laravel application tables into the public schema.
I kept Supabase's existing system schemas intact rather than attempting to replace the entire database.
After the restore, I verified that the expected application tables and records were present.
I also checked the Laravel migrations table to confirm that the database retained its migration history.
This was important because I wanted Laravel to recognize the existing database structure rather than recreate everything from scratch.
5. Connecting Laravel Cloud to Supabase
After importing the database, I updated my Laravel application's PostgreSQL connection settings.
The configuration followed the usual Laravel database environment variables:
DB_CONNECTION=pgsql
DB_HOST=YOUR_SUPABASE_POOLER_HOST
DB_PORT=5432
DB_DATABASE=postgres
DB_USERNAME=YOUR_SUPABASE_POOLER_USERNAME
DB_PASSWORD=YOUR_SUPABASE_DATABASE_PASSWORD
DB_SSLMODE=require
The database name remains postgres, which is Supabase's default PostgreSQL database.
I didn't need a separate database named after my application.
After updating the environment configuration, I cleared Laravel's configuration cache:
php artisan config:clear
Then I checked the migration status:
php artisan migrate:status
Laravel was able to read the migration records from the new database.
The existing models, relationships, controllers, and application queries didn't require a rewrite just because I changed the database hosting provider.
That was one of the biggest benefits of keeping the same database engine.
My Laravel application didn't need to know whether PostgreSQL was running on Laravel Cloud or Supabase. It only needed a working database connection.
For a production migration, it's also important to pause application writes during the final export and restore so that new records aren't left behind in the original database.
6. The issues I encountered after migrating
The database migration itself wasn't the only thing I needed to consider.
Moving to a different infrastructure provider also exposed a few configuration details that are easy to overlook.
Supabase Row Level Security warnings
After importing my Laravel tables, Supabase's Security Advisor reported several warnings about Row Level Security (RLS) being disabled.
These included tables used by my application, such as blog records and cache-related tables.
The warnings made sense because Supabase can expose database tables through its autogenerated Data API.
However, ShipWithIan doesn't use Supabase as its frontend API.
My architecture is:
React
|
v
Laravel
|
v
Supabase PostgreSQL
Laravel handles authentication, authorization, validation, and database operations.
I don't need my React frontend to query PostgreSQL through Supabase's Data API.
For this architecture, I disabled the Data API and kept database access behind Laravel.
This reduces an unnecessary exposure path, although direct database privileges and other security settings still need to be configured correctly.
RLS remains useful for applications that access Supabase directly from the frontend, but that isn't how ShipWithIan currently works.
Object storage was a separate concern
Another issue I encountered involved file storage.
After changing the database connection, an AWS SDK error appeared:
The GetObject operation requires
non-empty parameter: Bucket
This reminded me that moving a database doesn't automatically move the application's uploaded files.
PostgreSQL stores records and references, while actual uploaded images and documents may live in a separate object storage service.
I corrected the storage configuration independently of the database migration.
This is an important distinction when moving infrastructure: database hosting, application hosting, caching, and file storage are separate concerns.
Redis and application caching
I also needed to verify that my existing caching configuration would continue working.
Changing the PostgreSQL connection doesn't automatically change a separate Redis connection.
For applications that use database-backed caching, sessions, or queues, however, those components need to be checked because they may now operate against the new PostgreSQL database.
The lesson was simple: changing one infrastructure component requires checking every service that depends on it.
7. My current architecture
After the migration, my application infrastructure became more distributed.
Visitors
|
v
Laravel Cloud
|
+----------+----------+
| | |
v v v
Laravel Redis Object
+ React Cache Storage
|
|
v
Supabase PostgreSQL
Laravel Cloud continues to host the application.
Laravel still handles authentication, APIs, background jobs, notifications, and business logic.
Supabase is responsible for PostgreSQL.
I haven't moved my application logic into Supabase Edge Functions because I don't currently have a compelling reason to duplicate functionality that Laravel already provides.
This setup lets me use each platform for the specific service I need without committing my entire application to a single provider.
8. Did moving to Supabase actually save money?
For my current workload, the migration has reduced my immediate database hosting costs.
I no longer need to maintain the separate Laravel Cloud serverless PostgreSQL resource for ShipWithIan.
Instead, I'm using Supabase's Free database tier while my application remains within its included resource limits.
However, I don't consider this a guarantee that Supabase will always be the cheaper option.
There are several factors I need to monitor as the application grows:
Database storage. The Free plan has a 500 MB database size limit. This is a total storage allowance, not an additional 500 MB every month.
Network egress. My Laravel application runs on Laravel Cloud, while PostgreSQL runs on Supabase. Database results transferred from Supabase to Laravel Cloud contribute to network usage.
Database performance. The Free plan uses shared compute resources. As database queries and concurrent workloads increase, the instance may eventually become a performance bottleneck.
Availability and backups. Free projects can be paused after inactivity, and I need to maintain appropriate backups and recovery procedures instead of assuming that a free database has the same operational guarantees as a paid production database.
Network latency. Separating Laravel and PostgreSQL introduces network communication between providers. I need to monitor database response times and avoid inefficient queries that repeatedly transfer unnecessary data.
The real cost comparison isn't just the monthly database bill.
It also includes performance, reliability, operational effort, and the cost of maintaining the application as it grows.
For now, the migration better matches my current needs.
9. What I learned
This migration reinforced a few things about building and maintaining software independently.
Infrastructure should match the current workload
It's easy to choose services based on what an application might need when it becomes much larger.
But an application with relatively modest traffic doesn't necessarily need the same infrastructure as a high-traffic production platform.
I can design ShipWithIan to grow without paying for every possible future requirement today.
Managed services are convenient, but convenience has a cost
Keeping the application and database on Laravel Cloud simplified the original setup.
Separating them gives me more flexibility, but it also means managing connections, monitoring network usage, and understanding the limitations of each provider.
There is a trade-off between convenience and control.
PostgreSQL portability is valuable
Because I used PostgreSQL on both platforms, I could preserve my existing tables, data, migrations, and application queries.
I didn't need to rewrite my application around a different database system.
Using standard, widely supported technologies gives me options when my hosting requirements change.
Don't migrate everything just because you migrate one thing
Moving PostgreSQL to Supabase didn't mean I had to adopt Supabase Auth, Edge Functions, Realtime, or Storage.
Laravel already provides many of the capabilities my application needs.
I can introduce additional services later when they solve an actual problem.
There's no reason to add architectural complexity simply because a platform makes more features available.
Final thoughts
Moving ShipWithIan's PostgreSQL database from Laravel Cloud to Supabase was primarily a cost optimization decision.
I wanted to reduce my infrastructure expenses while keeping the application architecture I had already built.
The migration also gave me practical experience with PostgreSQL backup and restore, connection pooling, database security, storage configuration, and separating application infrastructure across providers.
I'm continuing to host ShipWithIan on Laravel Cloud while using Supabase for PostgreSQL.
As the application grows, I'll continue monitoring performance, resource usage, and actual costs to determine whether this setup remains appropriate.
For now, the biggest lesson is that building a scalable application doesn't mean paying for large-scale infrastructure before you need it.
Sometimes, the most useful optimization isn't changing your code.
It's choosing infrastructure that better fits the application you're running today.
I'm documenting the development and infrastructure decisions behind ShipWithIan as I continue building and improving the platform.
Website: shipwithian.online
Projects: shipwithian.online/projects