- Configuring Laravel to use the Supabase Postgres database involves correctly adjusting the driver, schema, and environment variables.
- Supabase's specific driver for Laravel automatically resolves common problems with UUID columns in queries and joins.
- The Flysystem adapter allows you to treat Supabase Storage as just another Laravel disk, easily integrating file uploads.
- Using privileged service keys and well-configured buckets is key to avoiding write errors and ensuring a stable flow.

If you work with Laravel and are dedicated to backend programming , and you're looking to make the leap to a modern, managed PostgreSQL database like Supabase , you've probably realized that simply changing a couple of variables in the .env file isn't enough. There are connection details, schemas, authentication, and file storage that, if neglected, can lead to some rather cryptic errors.
Furthermore, when you want to go a step further and use Supabase as file storage integrated with Laravel's disk system (Storage), things get a bit more complicated: service keys, buckets, endpoints, custom Flysystem drivers, etc. The good news is that everything can be perfectly integrated—the database, storage, and UUID handling—in a fairly clean way.
Connecting Laravel to the Supabase database
The first step is to have a working Laravel project and link it to the Postgres database provided by Supabase. For this, you need an environment with updated PHP and Composer , and you can create a new project or use an existing one. From the console, simply generate the project using the standard Laravel command and then begin setting up the connection.
Once you have the project's framework in place, the usual practice is to install a simple authentication system. Laravel Breeze fits in very well because it includes Blade templates and a basic login and registration flow , allowing you to quickly validate that your database connection is properly configured and that you can create users without issues.
To obtain the connection details, log in to your Supabase dashboard and create a new database project (you can do this directly from `database.new` , which redirects to the wizard). If you don't yet have an account, you'll first see the sign-up screen; if you already have one, you'll go directly to the project settings and the section where you can find the connection string.
Within the project page, in the connection section, you'll find a "Connect" button or something similar. Clicking it will display several connection string formats (URI, individual parameters, etc.). Copy the complete URI, but remember to replace the password with the one you actually use for the database, as a default password or placeholder is often displayed.
With that information, you need to go to your Laravel project's .env file and update the DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, and DB_PASSWORD variables, or configure the DATABASE_URL variable if you prefer to use the full string format. The goal is to ensure that everything points to the Supabase Postgres cluster and not to your localhost.
Configure the Postgres driver and the Supabase schema in Laravel
In Laravel, the key file for database configuration is config/database.php . Although you can directly use the pgsql driver that comes with the framework, when working with Supabase it's common to apply some additional settings, especially regarding schemas and Postgres-specific options.
A typical Postgres configuration in Laravel might look like this, within the connections array, under the key 'pgsql' :
'pgsql' => ,
The key here lies in the `search_path` parameter . Supabase, by default, uses the `public` schema, which is the one exposed through its APIs. If you want to keep your Laravel application separate from that schema and avoid table or policy conflicts, it's highly recommended to change the `search_path` to your own schema, for example, `laravel` , as seen in the previous example.
This way, migrations and tables generated by your project will be created in this alternative schema and not in public. This separation greatly simplifies managing security rules, RLS, and access from the Supabase panel without overwriting anything you shouldn't, while keeping the database structure organized.
Once you've adjusted the configuration file, you can run the migrations using standard Laravel commands. This will create the authentication tables and any other tables you've defined. If everything is configured correctly, the commands will run against the Supabase Postgres server automatically.
With the migrations complete, start the development server with `artisan serve` and try registering and logging in users. If no connection or migration errors appear, it means that Laravel is communicating correctly with Supabase and you can continue building your business logic as usual.
Using a specific Supabase driver in Laravel
Although the standard pgsql driver works, there is a package that adds a supabase database driver for Laravel , extending PostgreSQL's behavior with very useful improvements, especially regarding the handling of UUID columns and the way queries are constructed.
This package, distributed as prahsys/laravel-supabase, is installed via Composer and registers an additional driver called supabase that you can use in your config/database.php file. Internally, it's based on Laravel's Postgres driver, but incorporates settings and query grammars optimized for the specific Supabase environment.
Once installed, you could declare something like the following within the connections section :
'connections' => ,
// otras conexiones...
],
The advantage of using this driver is that you still get the full power of the Postgres engine, but it also automatically handles certain delicate Supabase details, especially when working with UUIDs as primary keys or relationship fields . This avoids having to manually write casting in every complex query.
The package is also designed to fit well with modern versions of the framework and PHP, offering official compatibility with Laravel 10.x, 11.x and 12.x and with PHP from version 8.1 onwards, as well as any standard PostgreSQL database, including of course Supabase.
To ensure everything works correctly, the package includes a suite of automated tests. You can run the tests with the `composer test` command, which will use an in-memory SQLite database for speed, or prepare an `.env.testing` file pointing to your Supabase and run `composer test-supabase` to verify the behavior in a real-world environment with remote Postgres.
UUID management in Supabase and Laravel
Supabase has a peculiarity with UUID columns: if you try to compare a UUID directly with a text string without casting, the query may fail or return unexpected results . In a bare Postgres environment, you could solve this with global casts or custom operators, but Supabase does not allow such global customizations.
This implies that a direct query of the style:
SELECT *
FROM users
WHERE id = '123e4567-e89b-12d3-a456-426614174000';
It won't work as you might expect. However, if you do the explicit casting:
SELECT *
FROM users
WHERE CAST(id AS TEXT) = '123e4567-e89b-12d3-a456-426614174000';
The query is successful. The problem is that in Laravel, when you write queries with Eloquent or the query builder, you don't want to add CASTs to every WHERE clause . That's where the supabase driver from the aforementioned package comes in, which adds those casts for you.
With that driver active, you can perform common queries such as:
$user = User::find($uuidString);
$user = User::where('id', $uuidString)->first();
$users = User::whereIn('id', )->get();
And not only in direct queries, but also in joins . For example, if you want to retrieve posts and join them with the users table using a UUID field, you could do something like this:
$posts = Post::join('users', 'posts.user_id', '=', 'users.id')
->where('users.email', '[email protected]')
->get();
The driver handles applying the necessary text casts to the relevant UUID columns transparently. This way, your code remains idiomatic for Laravel, and you don't need to write raw SQL or use strange tricks in every complex query.
If you want finer control over which columns are considered UUIDs, the package offers the CastsUuidColumns trait for your Eloquent models. Simply use it in your model class and define a protected array of additional columns:
use Prahsys\Supabase\Traits\CastsUuidColumns;
class Post extends Model
{
use CastsUuidColumns;
protected $uuidColumns = ;
}
This trait does three important things: it includes the primary key as the default UUID, adds any columns you declare to the `$uuidColumns` property , and communicates this information to the query builder so it knows where to apply the casts. This makes all data access involving UUIDs consistent and automated.
For even more advanced cases , you can register a custom UUID column detector. Using the PostgresGrammar from the package, you can specify a callback function that, based on the column name or query context, decides whether it should be treated as a UUID. For example:
use Prahsys\Supabase\Database\Query\Grammars\PostgresGrammar;
PostgresGrammar::detectUuidColumnsWith(function ($columnName, $query) {
return str_contains($columnName, 'uuid_')
|| in_array($columnName, );
});
With this function set up, the system can consider as UUID all columns whose name has a certain pattern or is within a specific list, adapting to very specific naming conventions of your project.
Integrate Supabase Storage as a file system in Laravel
In addition to the database, many projects need to store images, documents, or other files uploaded by users. Supabase includes a bucket-based storage service that you can use as Laravel's disk via Flysystem . A specific adapter is available to treat Supabase Storage as an additional driver within config/filesystems.php.
The package in question provides a Flysystem adapter that integrates seamlessly with the framework's Storage system. It meets the minimum requirements of PHP >= 8.1, Laravel 10.x and 11.x , and the PHP fileinfo extension (ext-fileinfo), which Laravel typically recommends for file handling. Installation is done using Composer, and once included, you only need to define the supabase disk in the configuration.
In the config/filesystems.php file , within the disks array, you would add something similar to the following:
'supabase' => ,
],
'signedUrlExpires' => 60 * 60 * 24,
],
The `bucket` parameter is usually simply the name of the storage bucket you created in the Supabase dashboard (for example, `myapp-file-uploads`). The `endpoint` is the base URL of the project's storage service, also visible in the corresponding section of the dashboard, and is usually derived from the project URL and region.
The `public` option indicates whether the bucket's contents will be treated as public by default. If `true`, the adapter will generate accessible URLs without a special signature; if `false`, the `defaultUrlGeneration` option comes into play, which can force the generation of signed URLs with an expiration time specified by `signedUrlExpires`. This configuration allows you to balance security and convenience depending on the type of files you handle.
The general disk URL is normally left as null so that the adapter automatically derives it from the endpoint. You should only modify it if you are using an intermediary proxy or CDN and want the generated routes to point to that domain instead of the native Supabase domain.
Troubleshoot upload errors and understand the key to Supabase Storage
A fairly common problem when trying to upload files to Supabase Storage from Laravel is receiving messages like “Unable to write file at location: uploads/…” . This usually indicates that, although the driver is configured, Supabase is denying the write operation due to insufficient permissions or an incorrect key configuration.
In the supabase disk configuration file, `config/filesystems.php`, the configuration specifies using a "privileged key" in the `key` field, explicitly stating that a read-only key will not work. This means you need to use a service key with write permissions to the bucket, not simply a client-side public API key or an S3 compatibility key without modification permissions.
In the Supabase panel, within the API and storage configuration section, you'll find both anonymous keys and service_role keys (or their equivalents) , which have extended privileges. It's this service key, not the public one, that you should place in the SUPABASE_SECRET_ACCESS_KEY variable, which the driver will then read using env('SUPABASE_SECRET_ACCESS_KEY').
If you've been testing with the S3 key from the storage configuration or with the project's generic API keys, it's very likely that those credentials don't have write permissions to the specific bucket, resulting in the write error. Changing the key value to a valid service key with write permissions and confirming that the bucket exists and is correctly named usually resolves the issue.
In addition to the password, it's important to verify that the bucket defined in SUPABASE_STORAGE_BUCKET exactly matches the one created in the Supabase interface, respecting uppercase and lowercase letters, and that the endpoint corresponds to the correct URL for that storage instance. A detail such as an extra character or an incorrect domain can prevent the adapter from locating the actual destination of the files.
Workflow with Laravel Breeze, Blade, and Supabase Storage
Once you have your database and storage configured, the next logical step is to integrate everything with your Laravel Breeze interface and Blade templates . This way, users can register, authenticate, and upload files to Supabase without leaving the Laravel ecosystem.
Within your controllers, you would use the Storage facade pointing to the supabase disk. For example, to upload a file received from a form with a file input, you could do something like this:
if ($request->hasFile('file')) {
$path = $request->file('file')
->store('uploads', 'supabase');
}
This code tells Laravel to use the Supabase disk and place the file inside the virtual uploads folder within the configured bucket. If the key and endpoint are correct, the file will be uploaded to Supabase storage, and you can retrieve its path or generate public or signed URLs using standard storage methods.
The advantage of this approach is that your application maintains a single interface for storage, regardless of whether it's using local disk, Amazon S3, Supabase, or another supported service. Switching providers is reduced to adjusting config/filesystems.php and environment variables, without affecting the business logic.
By combining this with Blade and Breeze, you can offer upload forms, file listings, and download links fully integrated into your application's user experience. Furthermore, Supabase Storage's bucket- and policy-based approach allows you to leverage its access controls and security rules to define what each user can view or download.
This entire ecosystem of packages, drivers, and configurations allows Laravel to work seamlessly with Supabase, both in terms of relational data with Postgres and file storage and UUID management . By properly configuring the keys, schemas, and drivers, you achieve a very robust integration that avoids many of the typical errors encountered when trying to connect both platforms manually without these supporting layers.
Connecting Laravel with Supabase for database and storage, leveraging the dedicated Supabase driver to manage UUIDs without headaches, and using the Flysystem adapter for storage allows you to build modern applications where all the complex infrastructure is encapsulated behind Laravel's clean API, from migrations and authentication to uploading files to secure buckets.