> A Supabase row-level security checklist: policies per operation, grants after the October 2026 change, teams, functions, realtime, storage, keys and tests.

Source: https://easifytechnologies.com/supabase-rls-checklist/

[Insights](https://easifytechnologies.com/insights/)  [Articles](https://easifytechnologies.com/insights/article/)

# Supabase RLS Checklist: Policies, Tests and the Service Role Key

20 October 2026 16 min read

![Isometric illustration of a person placing a code block on a laptop screen, with a shield and padlock, a cloud upload icon and gears around it](https://res.cloudinary.com/c3ccc6ni/image/upload/f_auto,q_auto,c_limit,w_1400/easify/2025/06/4799410-scaled)

A safe Supabase setup needs row-level security enabled on every table in an exposed schema, grants that give clients only what they need, a separate policy for each operation, ownership checked with both `using` and `with check`, no authorisation data that users can edit, functions, views, realtime and storage covered, the secret or `service_role` key kept off the client, and tests that prove two users cannot reach each other's rows. Enabling RLS is the start, not the finish: a policy of `true` passes a glance and protects nothing.

This checklist is for anyone building on Supabase, whether by hand, with a framework, or through an AI builder such as Lovable or Bolt. The SQL follows Supabase's own documentation, checked on 20 October 2026. One date matters this month: from **30 October 2026**, Supabase stops granting API access to new tables automatically on existing projects. Item 3 explains what that changes.

Work with us

Want someone to read your RLS policies against the live app?

[Start a conversation _↗_](https://easifytechnologies.com/contact/) [Prototype to Production](https://easifytechnologies.com/services/prototype-to-production/)

## At a glance

Item

Rule

Which tables

Every table in an exposed schema, usually `public`

Grants

New tables need explicit grants on new projects since 30 May 2026 and on all projects from 30 October 2026

Policies

One per operation: select, insert, update, delete

Who

Always name the role with `to authenticated` (or `anon`)

Ownership

`using` checks the existing row; `with check` checks the new one

Updates

Need a matching select policy, or they silently do nothing

Teams

A membership table, read through a `security definer` function

Performance

Wrap `auth.uid()` as `(select auth.uid())` and index the columns policies filter on

Authorisation data

In `app_metadata` or your own tables, never `user_metadata`

Functions

`security definer` only in a private schema, with `search_path = ''` and execute revoked from `public`

Views

`security_invoker = true`, or keep them out of exposed schemas

Realtime

Postgres Changes follow RLS; Broadcast and Presence need `realtime.messages` policies

Keys

Publishable/anon in the client; secret/`service_role` only on a server

Tests

pgTAP with `supabase test db`, plus a manual two-user test

Automated check

Supabase's Security Advisor

## Why RLS carries so much weight

When a browser or mobile app calls Supabase with the publishable (or legacy anon) key, the request runs as the `anon` role, or as `authenticated` once a user signs in. Supabase's [API key guide](https://supabase.com/docs/guides/api/api-keys) says these keys are fine to ship in public code, provided RLS is on. That makes your policies the only thing between any visitor and your tables.

This is not theoretical. [CVE-2025-48757](https://nvd.nist.gov/vuln/detail/CVE-2025-48757) records apps generated by Lovable whose tables could be read or written without logging in because their RLS was insufficient. Lovable disputes the record, and the cause is not specific to Lovable: any Supabase project with the same policies has the same exposure. More on that case in [is Lovable safe?](https://easifytechnologies.com/is-lovable-safe/)

## Grants and policies are two different locks

It helps to keep two layers apart, because Supabase's October change makes the difference visible:

-   **Grants** decide whether a role (`anon`, `authenticated`, `service_role`) may touch a table at all, and for which operations.
-   **Policies** decide which rows that role sees or changes, once a grant lets it in.

A request needs both. A missing grant gives a loud error (`42501`, "permission denied for table"). A missing policy, with RLS on, gives a quiet one: no rows. A policy of `true`, or RLS off, gives no error at all, which is why it is the dangerous one.

![A Supabase request passes a grant check, then RLS, then a policy and its row filter: a missing grant returns error 42501, a missing policy returns an empty list, and RLS off or a policy of true returns every row with no error.](https://easifytechnologies.com/img/diagrams/supabase-request-security-path.svg)

## The checklist: tables and grants

### 1\. Find every table where RLS is off

Start with an inventory. This query uses Postgres's own catalogue and lists every table in `public` with its RLS state:

```sql
select tablename, rowsecurity
from pg_tables
where schemaname = 'public'
order by rowsecurity, tablename;
```

Any row with `rowsecurity = false` is a table the API can read with the public key, if a grant allows it. Supabase's Security Advisor reports the same thing as `0013_rls_disabled_in_public`.

### 2\. RLS enabled on every table in an exposed schema

Supabase's [RLS guide](https://supabase.com/docs/guides/database/postgres/row-level-security) is blunt: "Enable RLS on every table in an exposed schema." Check new tables especially, including ones created by a migration or an AI prompt. The same guide points to event triggers if you want RLS switched on automatically for every new table.

```sql
alter table public.reports enable row level security;
```

### 3\. Grants given deliberately, not by default

Until this year, a new table in `public` started with select, insert, update and delete granted to `anon`, `authenticated` and `service_role`, and the guide warns that "adding policies doesn't take those grants back." Supabase's [changelog](https://supabase.com/changelog/45329-breaking-change-tables-not-exposed-to-data-and-graphql-api-automatically) changes that: "New tables in the public schema will no longer be exposed to the Data API automatically." It became the default for new projects on 30 May 2026 and is applied to all existing projects on 30 October 2026. Existing tables keep their current grants; direct Postgres connections are not affected.

What to do:

-   **For new tables,** add explicit grants in the same migration that creates the table, giving each role only what it needs. If `anon` should never read it, do not grant it.
-   **For existing tables,** the old broad grants are still there. Revoke what clients should not have.
-   **If a new table answers `42501`,** that is the change working. The error's hint names the grant; add it on purpose, then check the policies.

```sql
-- A table only signed-in users touch:
revoke all on table public.reports from anon, authenticated;
grant select, insert, update, delete on table public.reports to authenticated;
```

## The checklist: policies

### 4\. One policy per operation, with the role named

Postgres does not accept several operations in one `for` clause, so write four policies, and always say who they apply to. Naming `to authenticated` also stops the policy running at all for signed-out users.

```sql
create policy "Users can view their own profile."
on profiles for select
to authenticated
using ( (select auth.uid()) = user_id );

create policy "Users can create their own profile."
on profiles for insert
to authenticated
with check ( (select auth.uid()) = user_id );

create policy "Users can update their own profile."
on profiles for update
to authenticated
using ( (select auth.uid()) = user_id )
with check ( (select auth.uid()) = user_id );

create policy "Users can delete their own profile."
on profiles for delete
to authenticated
using ( (select auth.uid()) = user_id );
```

### 5\. `with check` on every insert and update

`using` decides which existing rows a user may touch. `with check` decides what the row may look like afterwards. Without it, a user can insert a row claiming to belong to someone else, or update their own row to move it to another owner.

### 6\. Updates have a matching select policy

Supabase's guide: "To perform an UPDATE operation, a corresponding SELECT policy is required." Without one, the update matches no rows and returns no error, which is confusing in development and tempts people into loosening the wrong policy.

### 7\. No policy that just says `true`, or only checks login, on private data

`using (true)` is right for genuinely public content, such as published blog posts. On anything private it is the same as RLS being off. Its quieter cousin is `using (auth.uid() is not null)`: it keeps out anonymous visitors but lets every signed-in user read every row. Search your migrations for both, and for policies that compare against a value the client sends.

### 8\. Team and organisation access goes through a membership table

Most B2B apps need "members of the same team can see this", which per-user policies cannot express. Supabase's guide shows a membership table keyed on team and user, read through a `security definer` function so that policies on the membership table itself do not recurse. A sketch of that pattern:

```sql
create table public.team_members (
  team_id uuid not null references public.teams (id) on delete cascade,
  user_id uuid not null references auth.users (id) on delete cascade,
  role text not null default 'member',
  primary key (team_id, user_id)
);
create index team_members_user_id_idx on public.team_members (user_id);
alter table public.team_members enable row level security;

create schema if not exists private;
grant usage on schema private to authenticated;

create function private.my_team_ids()
returns setof uuid
language sql
stable
security definer
set search_path = ''
as $$
  select team_id from public.team_members
  where user_id = (select auth.uid());
$$;

revoke execute on function private.my_team_ids() from public;
grant execute on function private.my_team_ids() to authenticated;

create policy "Team members can view projects"
on public.projects for select
to authenticated
using ( team_id in (select private.my_team_ids()) );
```

Then decide, and test, who may add members. If ordinary members can insert rows into `team_members`, any member can add themselves to any team. Membership changes usually belong in server code or a policy limited to team admins.

The alternative the guide also shows is reading team IDs from `app_metadata` in the JWT (item 10). It avoids a lookup, but a removed member keeps access until their token refreshes.

### 9\. Columns users must not change are protected

A user who may update their own `profiles` row may also be able to update its `role`, `plan` or `credits` column. Keep those in a separate table that clients cannot write, or change them only from server code. Row-level security works on whole rows, which is why the researcher's [write-up of the Lovable CVE](https://mattpalmer.io/posts/CVE-2025-48757/) recommends splitting sensitive and non-sensitive data into tables with their own policies.

### 10\. Authorisation never read from `user_metadata`

The guide warns that `raw_user_meta_data` "can be updated by the authenticated user" and "is not a good place to store authorization data." Use `app_metadata`, which users cannot edit, or your own tables:

```sql
create policy "User is in team"
on my_table
to authenticated
using ( team_id in (select auth.jwt() -> 'app_metadata' -> 'teams') );
```

Remember that a JWT is not refreshed instantly, so a removed team membership can linger until the token renews. Security Advisor flags policies that read user metadata as `0015_rls_references_user_metadata`.

## The checklist: functions, views, realtime and storage

### 11\. Helper functions are `security definer` done properly

For role checks that need another table, Supabase shows a `security definer` function in a private schema, with an empty `search_path` and schema-qualified names. The guide warns that "a security definer function in an exposed schema is callable over the Data API with the creator's privileges", which makes it a way around every policy.

```sql
create function private.has_good_role()
returns boolean
language plpgsql
security definer
set search_path = ''
as $$
begin
  return exists (
    select 1 from public.roles_table
    where (select auth.uid()) = user_id and role = 'good_role'
  );
end;
$$;

create policy "rls_test_select"
on test_table
to authenticated
using ( (select private.has_good_role()) );
```

### 12\. Functions the API can call check permissions themselves

Postgres lets everyone execute a new function unless you revoke it. Any function in `public` is reachable as an RPC call from the browser. For each one:

-   revoke execute from `public` and grant it only to the roles that need it, as the guide's example does;
-   if it is `security definer`, move it out of the exposed schema, or make it check `auth.uid()` and ownership before doing anything;
-   validate its arguments, because the caller chooses them.

```sql
revoke execute on function public.archive_project(uuid) from public;
grant execute on function public.archive_project(uuid) to authenticated;
```

### 13\. Views do not route around the policies

"Views bypass RLS by default because they are usually created with the `postgres` user." On Postgres 15 and later, create them like this; on older versions, revoke access from `anon` and `authenticated` or move them to a schema the API does not expose. Security Advisor reports the risky kind as `0010_security_definer_view`.

```sql
create view public.my_reports
with (security_invoker = true)
as select * from public.reports;
```

### 14\. Realtime channels are authorised too

Supabase's [Realtime authorization guide](https://supabase.com/docs/guides/realtime/authorization) says that for Postgres Changes, "database records are sent only to clients who are allowed to read them based on your RLS policies", so your table policies already apply. Broadcast and Presence are different: they are controlled by policies on `realtime.messages`, and only on private channels. To enforce them, turn off "Allow public access" in Realtime settings and create channels with `private: true` on the client. Permissions are checked when a client joins and cached for the connection, so revoking access takes effect when the JWT expires or a new one is sent.

### 15\. Storage buckets have policies too

Files live in `storage.objects`, which has its own RLS. Supabase's [storage access control guide](https://supabase.com/docs/guides/storage/security/access-control) says uploads are refused by default unless a policy allows them, and that public buckets need no policy to be read. Keep private documents out of public buckets, and scope uploads to a folder named after the user:

```sql
create policy "Users upload to their own folder"
on storage.objects for insert
to authenticated
with check (
  bucket_id = 'documents'
  and (storage.foldername(name))[1] = (select auth.jwt()->>'sub')
);
```

Add matching select, update and delete policies for the same folder.

### 16\. Policies are fast enough to keep

Wrap auth functions in `select` so Postgres evaluates them once per statement rather than once per row, and index the columns policies filter on. Supabase warns that "an unindexed filter column turns a read into a sequential scan."

```sql
create index profiles_user_id_idx
on public.profiles
using btree (user_id);
```

## The checklist: keys and the project around the policies

### 17\. Secret and `service_role` keys stay on a server

Supabase's secret keys (`sb_secret_...`) and the legacy `service_role` key run with `BYPASSRLS`: no policy applies to them. The [API key guide](https://supabase.com/docs/guides/api/api-keys) says to use them only in backend code you control, such as servers with their own authorisation checks, Edge Functions and scheduled jobs, and "never put one in a browser, a shipped application, or source control."

Four consequences people miss:

-   **Server code with a secret key must check permissions itself.** RLS is no longer protecting it. An Edge Function that takes a record ID and updates it with the secret key must first confirm the caller owns that record.
-   **Bundlers publish prefixed variables.** Anything named `VITE_` or `NEXT_PUBLIC_` ends up in the JavaScript sent to the browser. A secret key there is public.
-   **The browser block is not protection.** The guide says Supabase refuses a secret key sent from a browser, matching on the `User-Agent` header, with HTTP 401. An attacker who copies the key uses another tool, so a key that reached a browser must still be deleted.
-   **A leaked key needs rotating, not just deleting from the code.** The guide's order: fix the cause, create a new secret key, replace it everywhere, confirm, then retire the old one. Use a separate secret key per backend component so one leak affects one component.

The same guide says Supabase is deprecating the legacy `anon` and `service_role` keys by the end of 2026, in favour of publishable (`sb_publishable_...`) and secret keys. Legacy keys keep working until you disable them, so plan the switch rather than waiting for it.

### 18\. Auth and project settings are hardened

Policies protect rows; these settings protect accounts and the database around them. From Supabase's [production checklist](https://supabase.com/docs/guides/deployment/going-into-prod):

-   enable email confirmations;
-   set the OTP expiry to 3,600 seconds (one hour) or lower;
-   turn on CAPTCHA protection for sign-up, sign-in and password reset;
-   use a custom SMTP server for auth emails;
-   review the rate limits on auth endpoints;
-   enable SSL enforcement and network restrictions for the database;
-   protect the Supabase account itself with multi-factor authentication.

The same page notes that database backups are not available for download on the Free plan, so keep an export of your own if you are on it.

## How to test your policies

### Automated: pgTAP

Supabase's RLS guide documents pgTAP tests under `supabase/tests/`, run with `supabase test db`, and says plainly: "Until the suite passes, you don't know whether the policies do what you intended." Inside a test, switch role and identity like this, then assert what that user sees:

```sql
set local role authenticated;
set local request.jwt.claim.sub = '11111111-1111-1111-1111-111111111111';
```

Write at least these cases for every private table:

Who

Select

Insert

Update

Delete

Signed out

No rows

Refused

No effect

No effect

Owner

Own rows

Own rows only

Own rows, cannot change owner

Own rows

Second signed-in user

No rows of the owner's

Cannot insert as the owner

No effect on the owner's rows

No effect on the owner's rows

Team member (if teams)

Team rows only

Into own team only

As designed

As designed

Ordinary user on role or plan columns

As designed

Refused

Refused

Refused

Supabase's guide also points to the `supabase-test-helpers` package, which adds helpers such as `tests.authenticate_as()`. Run the suite in CI on every migration.

### Manual: two users and a logged-out request

On your own project only:

1.  Make two test accounts. As user A, change an ID in the URL to user B's record, and try to view, edit and delete it.
2.  As an ordinary user, try to update your own role or plan.
3.  Call each RPC function signed out and as the wrong user.
4.  With no session, request a private table directly with the publishable key:

```bash
curl "https://YOUR-PROJECT.supabase.co/rest/v1/profiles?select=*" \
  -H "apikey: YOUR-PUBLISHABLE-KEY"
```

An empty array is the right answer for a private table. Rows mean the policy is wrong.

### The Security Advisor

Supabase's [Security Advisor](https://supabase.com/docs/guides/database/database-advisors), in the dashboard or via `supabase db advisors`, flags known problems, including `0013_rls_disabled_in_public` (a table anyone with the project URL can access), `0007_policy_exists_rls_disabled`, `0010_security_definer_view`, `0015_rls_references_user_metadata` and `0024_permissive_rls_policy`. Clear every error, and read the warnings. It checks configuration, not intent: it cannot know who should see your invoices.

## Common mistakes

-   **Enabling RLS and stopping there.** The app breaks, someone adds `true` to fix it, and the table is open.
-   **`auth.uid() is not null` as an ownership check.** It only proves the caller is logged in.
-   **An update policy with no `with check`.** The owner can hand a row to someone else, or take one.
-   **Testing as the dashboard user.** The SQL editor runs as `postgres`, the owner of tables created there, and RLS does not apply to a table's owner by default. Test as `anon` and `authenticated`.
-   **A `security definer` function in `public`.** It is an API endpoint that ignores your policies.
-   **Trusting the JWT for instant revocation.** Membership in `app_metadata` lasts until the token refreshes.
-   **Assuming views and storage follow table policies.** Each has its own rules.
-   **Loosening grants to silence `42501` after 30 October.** Grant what the feature needs, to the role that needs it.

## If you built with an AI tool

Lovable Cloud is built on Supabase, and Lovable and Bolt can both use your own Supabase project, so everything above applies. The tool-specific notes are in the guides for [Lovable](https://easifytechnologies.com/lovable-app-to-production/) and [Bolt](https://easifytechnologies.com/bolt-app-to-production/). Watch for policies an AI wrote to make an error disappear: they are often `true`. If something stopped working after a prompt, [what to do when a Lovable app stops working](https://easifytechnologies.com/lovable-app-not-working/) covers the empty-query and permission errors from the other side.

**When not to hire us:** if your app has one user type, owner-only data and you have passed the manual tests and the Security Advisor, you probably do not need a review. Keep the tests running.

If your model has teams, organisations, shared records or admin roles, that is where policies get subtle. The [€99 Prototype Check](https://easifytechnologies.com/prototype-check/) has a person read the policies and test them against the live app, with the top risks in writing within 48 hours of payment and access, then a 15-minute call. The €99 is credited against the build if you hire us. We are based in Budapest, working remotely across Europe on CET, with our engineering team in India.

Work with us

## Want someone to read your RLS policies against the live app?

We are a Budapest team that takes AI-built and fast-built apps to production for founders across Europe, working remotely on CET. The €99 Prototype Check includes a person testing your Supabase policies against the live app, with the top security and data risks in writing within 48 hours. Send the repository and live URL, and we reply within one business day.

[Start a conversation _↗_](https://easifytechnologies.com/contact/) [Prototype to Production](https://easifytechnologies.com/services/prototype-to-production/)

On this page

-   [At a glance](#at-a-glance)
-   [Why RLS carries so much weight](#why-rls-carries-so-much-weight)
-   [Grants and policies are two different locks](#grants-and-policies-are-two-different-locks)
-   [The checklist: tables and grants](#the-checklist-tables-and-grants)
-   [1\. Find every table where RLS is off](#1-find-every-table-where-rls-is-off)
-   [2\. RLS enabled on every table in an exposed schema](#2-rls-enabled-on-every-table-in-an-exposed-schema)
-   [3\. Grants given deliberately, not by default](#3-grants-given-deliberately-not-by-default)
-   [The checklist: policies](#the-checklist-policies)
-   [4\. One policy per operation, with the role named](#4-one-policy-per-operation-with-the-role-named)
-   [5\. with check on every insert and update](#5-with-check-on-every-insert-and-update)
-   [6\. Updates have a matching select policy](#6-updates-have-a-matching-select-policy)
-   [7\. No policy that just says true, or only checks login, on private data](#7-no-policy-that-just-says-true-or-only-checks-login-on-private-data)
-   [8\. Team and organisation access goes through a membership table](#8-team-and-organisation-access-goes-through-a-membership-table)
-   [9\. Columns users must not change are protected](#9-columns-users-must-not-change-are-protected)
-   [10\. Authorisation never read from user\_metadata](#10-authorisation-never-read-from-user-metadata)
-   [The checklist: functions, views, realtime and storage](#the-checklist-functions-views-realtime-and-storage)
-   [11\. Helper functions are security definer done properly](#11-helper-functions-are-security-definer-done-properly)
-   [12\. Functions the API can call check permissions themselves](#12-functions-the-api-can-call-check-permissions-themselves)
-   [13\. Views do not route around the policies](#13-views-do-not-route-around-the-policies)
-   [14\. Realtime channels are authorised too](#14-realtime-channels-are-authorised-too)
-   [15\. Storage buckets have policies too](#15-storage-buckets-have-policies-too)
-   [16\. Policies are fast enough to keep](#16-policies-are-fast-enough-to-keep)
-   [The checklist: keys and the project around the policies](#the-checklist-keys-and-the-project-around-the-policies)
-   [17\. Secret and service\_role keys stay on a server](#17-secret-and-service-role-keys-stay-on-a-server)
-   [18\. Auth and project settings are hardened](#18-auth-and-project-settings-are-hardened)
-   [How to test your policies](#how-to-test-your-policies)
-   [Automated: pgTAP](#automated-pgtap)
-   [Manual: two users and a logged-out request](#manual-two-users-and-a-logged-out-request)
-   [The Security Advisor](#the-security-advisor)
-   [Common mistakes](#common-mistakes)
-   [If you built with an AI tool](#if-you-built-with-an-ai-tool)

Frequently asked

Is enabling RLS on a Supabase table enough?

No. Enabling RLS with no policies blocks access through the API, which is safe but useless. Enabling it with a policy of true lets everyone through, which looks configured but protects nothing. You need a policy per operation that limits rows to the right users, and a test that proves it.

Is RLS enabled by default in Supabase?

Do not rely on it. Supabase's guide says to enable RLS on every table in an exposed schema and points to an event trigger if you want it switched on automatically for new tables. Tables created by raw SQL, migrations or an AI tool are the ones to check. A query on pg\_tables lists every public table where it is off.

What changes in Supabase on 30 October 2026?

Supabase's changelog says new tables in the public schema are no longer exposed to the Data API automatically. That became the default for new projects on 30 May 2026 and applies to all existing projects on 30 October 2026. Existing tables keep their grants. New tables need explicit GRANT statements, and RLS still decides which rows each role can see.

Can I put the Supabase anon or publishable key in my front end?

Yes. Supabase's API key guide says publishable and anon keys are safe in browsers, mobile apps and public source code, provided RLS is enabled on every table. The secret and service\_role keys are different: they bypass RLS and belong only in backend code you control. Supabase also says the legacy anon and service\_role keys are being deprecated by the end of 2026.

Why does my Supabase update silently do nothing?

Supabase's RLS guide says an UPDATE needs a matching SELECT policy as well. Without one, the update affects no rows and returns no error. Add a select policy for the same users, then check the update policy has both using and with check clauses.

Do Supabase views and realtime respect row-level security?

Views do not by default, because they are usually created by the postgres user; on Postgres 15 or later create them with security\_invoker set to true. Realtime Postgres Changes are sent only to clients allowed to read the rows under your policies. Broadcast and Presence need their own policies on realtime.messages and private channels.

How do I test Supabase RLS policies automatically?

Supabase supports pgTAP tests in supabase/tests, run with supabase test db. Inside a test you switch to the anon or authenticated role with set local role, and set the user with set local request.jwt.claim.sub, then assert what that user can and cannot read or write.

Does this checklist apply to Lovable and Bolt apps?

Yes. Lovable Cloud is built on Supabase, Lovable can also use your own Supabase project, and Bolt can connect to Supabase. Any app whose front end or server code queries Supabase with the public key depends on these policies.

Keep reading

-   [

    ![](https://res.cloudinary.com/c3ccc6ni/image/upload/f_auto,q_auto,c_limit,w_560/easify/2023/07/87shots_so)

    ### Edtech MVP Development: Buyers, Children's Data and Scope

    ](https://easifytechnologies.com/edtech-mvp-development/)
-   [

    ![](https://res.cloudinary.com/c3ccc6ni/image/upload/f_auto,q_auto,c_limit,w_560/easify/2022/05/MVP-dev1-scaled)

    ### Enterprise Ireland Funding for Startups Building an MVP

    ](https://easifytechnologies.com/enterprise-ireland-funding-for-startups/)
-   [

    ![](https://res.cloudinary.com/c3ccc6ni/image/upload/f_auto,q_auto,c_limit,w_560/easify/2022/05/MVP-dev1-scaled)

    ### EU Funding for Startups: Can It Pay for Your MVP?

    ](https://easifytechnologies.com/eu-funding-for-startups-mvp/)

Want this done properly on your own site?

[Start a conversation _↗_](https://easifytechnologies.com/contact/)
