What to migrate first when you move your app off AWS
Moving off AWS doesn't have to be risky. Here's the safest migration order: deployment pipeline first, Redis second, database last.
Every week your team runs on self-managed infrastructure is a week the infrastructure tax is running. Two to four hours of routine maintenance. Unplanned incident response. Senior engineering time held in reserve for the operational problems that will inevitably surface.
Deployment anxiety that reduces how often the team ships. Backlog items that keep slipping because infrastructure work displaced them. The tax is not a one-time cost. It compounds, sprint after sprint, until the gap between what the team could be shipping and what it actually ships becomes the most expensive thing in the business.
Most teams that decide to move off self-managed infrastructure get stuck before they start. Not because the migration is technically beyond them, but because they look at the full picture at once: the database, the queues, the file storage, the deployment pipeline, the environment variables spread across Parameter Store and the console, the cron jobs running on that one specific instance, the Redis cluster that three different parts of the application depend on. The scope feels enormous and the path through it is not obvious.
This is for engineering leads and CTOs who have already made the decision to stop owning infrastructure they should never have been running in the first place. Not for teams still evaluating whether to move. For teams that know the answer and need a clear sequence for getting there without breaking production in the process.
The migration is more tractable than it looks. Every day spent not starting it is a day the tax keeps running.
The right mental model for migration#
Before sequencing anything, reframe what the migration actually is.
You are not rebuilding your application. The application code does not change. The database schema does not change. The business logic does not change. What changes is the operational layer that sits beneath the application: where it runs, who is responsible for keeping it running, and how your team interacts with it at deployment time.
That layer is the one your team should not have been managing in the first place. The database, the cache, the queue workers, the deployment pipeline: none of those are sources of competitive advantage. They are the operational substrate your product runs on top of, and every hour your engineers spend maintaining them is an hour not spent on the product. The migration is not a technical project. It is a decision to stop doing work that was never your team's job.
The risk in a migration is not in moving the application. It is in the transitions between states. The application running on self-managed infrastructure is stable. The application running on Sevalla is stable. The window where it is partially in both places is where care is required. The goal of the sequencing below is to minimise the time spent in that window for each component, moving the most impactful and least risky pieces first.
Start with the deployment pipeline#
The first thing to migrate is not the application itself. It is the deployment pipeline.
This sounds counterintuitive but the logic is straightforward. The deployment pipeline is the mechanism through which everything else gets migrated. If you start with the database or the application runtime and your deployment pipeline is still the old one, every subsequent step in the migration has to be coordinated between two different deployment systems. That complexity compounds quickly.
Moving the deployment pipeline first means moving to Git-based deployments on Sevalla before anything else changes. Your application still points at the same database, the same Redis, the same everything. The only thing that changes is how code gets from your repository to a running application. Your team pushes to Git. Sevalla builds and deploys. The old AWS deployment pipeline is retired.
On a Laravel 12 application running on PHP 8.5, your build steps in Sevalla's configuration look something like this:
build:
run:
- composer install --no-dev --optimize-autoloader
- php artisan config:cache
- php artisan route:cache
- php artisan view:cacheQueue workers and the scheduler move at this stage too. If your application uses Laravel queues and the scheduler, they are defined as part of the platform configuration rather than maintained as separate processes you are responsible for keeping running:
workers:
- name: queue
command: php artisan queue:work --sleep=3 --tries=3
crons:
- name: scheduler
schedule: "* * * * *"
command: php artisan schedule:runAfter this step, your team is already operating differently. Deployment is a Git push. The queue workers are managed by the platform. The scheduler runs without a cron server your team is responsible for. The deployment pipeline that was failing in AWS-specific ways, requiring AWS-specific knowledge to diagnose, requiring the one engineer who knew it to be available when it broke, is gone.
Your team can deploy on a Friday without elevated anxiety. New engineers can deploy on their first week. The infrastructure specialist is no longer a bottleneck on every release. The hours that were going into pipeline maintenance are now available for product work. That shift happens at step one, before you have moved a single byte of data.
Move Redis next#
Redis is the right second step because it has the smallest data portability concern of the stateful components. Session data is transient. Cache data is transient. The migration window for Redis is effectively zero because there is nothing in Redis that cannot be reconstructed immediately after the switch.
The migration is a configuration change: update the Redis connection string in your environment variables to point at the Sevalla-managed Redis instance instead of your ElastiCache cluster. Flush the old cache. Deploy. Done.
The one thing to verify before switching is that anything in your application that uses Redis for more than caching, specifically anything using it as a queue driver or for pub/sub, has a clear migration path. If you moved queue workers in the first step above and they are already pointing at a different queue backend, this is clean. If you are using Redis as a queue driver, the Redis migration and the queue driver configuration need to move together.
After this step, your ElastiCache cluster is retired. That is one fewer service your team is paying for, one fewer service generating alerts your team has to respond to, and one fewer component requiring access policy maintenance and version management. The operational surface shrinks. The cost of running the infrastructure drops. The team does not notice the change because Redis still works exactly as it did. That is what retiring infrastructure overhead is supposed to feel like.
Move the database last#
The database migration is the most significant step and it belongs last for a reason. By the time you get here, everything else is already running on Sevalla. The application runtime is on the platform. Redis is on the platform. The deployment pipeline is on the platform. The only remaining dependency on AWS infrastructure is the database.
That sequencing means you are migrating the database from a stable, known state rather than from a partially-migrated system. It also means that if the database migration requires a maintenance window, everything else is already in the right place and the window is genuinely just for the data transfer.
The migration itself is a standard database export and import followed by a connection string update. For a Laravel application:
# Export from your existing database
mysqldump -h your-rds-endpoint -u username -p database_name > migration.sql
# Import to your Sevalla-managed database
mysql -h sevalla-db-endpoint -u username -p database_name < migration.sqlVerify the import is complete and consistent before switching the connection string. Update your environment variables to point at the Sevalla-managed database. Deploy. Verify the application is running cleanly against the new database. Retire the RDS instance.
At this point, your team is operating entirely on Sevalla. No RDS, no ElastiCache, no ECS, no CloudWatch, no IAM policies to maintain, no deployment pipeline to debug. The AWS account is still there but it is no longer the operational layer your application depends on.
The infrastructure knowledge that lived in your most experienced engineers' heads is no longer load-bearing. The on-call rotation for infrastructure incidents is no longer a cost your team carries. The hiring profile for new engineers is no longer "must understand our AWS setup before contributing." The sprints that were losing capacity to infrastructure maintenance are now fully available for product work. The migration is complete and the tax has stopped.
The components that do not require migration#
S3 for file storage does not need to migrate at the same time as everything else. If your application stores files in S3, you can continue pointing at the same S3 bucket from Sevalla without any disruption. Migrate file storage separately after the primary migration is complete, if you decide to move it at all. The important thing is that S3 is not a blocking dependency for the rest of the migration.
Third-party services, external APIs, and anything that connects to your application over a network rather than running alongside it do not require migration. They work against the new environment the same way they worked against the old one. Connection strings and API keys move with the environment variables.
What the team gets immediately#
The change in how the team operates is not gradual. It is immediate after the deployment pipeline migration in step one, and it becomes total after the database migration in step three.
The engineer who was carrying the infrastructure context in their head no longer needs it for day-to-day operations. New engineers can deploy from their first week without an infrastructure orientation. The deployment pipeline cannot fail in an AWS-specific way because there is no AWS-specific deployment pipeline. Incidents that previously required tracing through multiple cloud services to diagnose now surface at the application layer where every engineer already has the context to respond.
The backlog items that kept getting pushed because infrastructure work ate the sprint are no longer being pushed for that reason. The roadmap moves at the speed the team is actually capable of rather than the speed the infrastructure tax allows.
The migration is not the risk you think it is#
The hesitation most teams feel about migrating is usually framed as technical risk. The migration is complex. The database move is scary. Something will break in production.
The technical steps above are not complex. They are a deployment configuration change, a connection string swap, and a database export and import. A senior engineer who knows the application can execute all three in a planned maintenance window with low risk and a clear rollback path at each stage.
The framing of migration as the risk is a trap. It focuses attention on a bounded, manageable, one-time project and away from the unbounded, ongoing, compounding cost of staying where you are.
Every sprint your team stays on self-managed infrastructure, the infrastructure tax runs. Two to four hours of maintenance. Incident response. Deployment debugging. Senior engineering time that could be going to the product going to the platform instead. Features that do not ship because the sprint got eaten. Architectural decisions not made because the team was in reactive mode. The backlog growing faster than it shrinks.
Every quarter you defer the migration, the migration gets harder. More services added to the stack. More institutional knowledge embedded in the people who built them. More engineers who have adapted their workflows around the infrastructure's quirks. The window for a tractable migration does not stay open indefinitely.
The real risk is not the migration. It is the cost of every month spent not doing it. That cost is running right now, and it will keep running until the decision is made.
Your team should not be owning this infrastructure at all. The migration is how you stop. Sevalla is where you land.