Avatar of Sherifdeen AwofiranyeSherifdeen Awofiranye

How to Migrate from AWS Lambda to Railway

If you are currently using AWS Lambda and are considering migrating to Railway, this article will walk you through a step-by-step process for migrating your AWS Lambda function to Railway.

While AWS Lambda is a powerful tool, there are a few reasons why developers might choose Railway for their projects:

  • Simplified Deployment: Railway abstracts away much of the complexity involved in deployment, providing a seamless experience that doesn’t require deep cloud infrastructure knowledge.
  • Built-in Database Support: Railway makes it easy to connect to and manage databases without needing separate services.
  • Cost-Effectiveness: For smaller projects or developers looking to optimize their spending, Railway offers simpler pricing models, especially for projects that require both hosting and databases.
  • Better Local Development: Railway’s integration with local development environments is straightforward, allowing you to deploy without worrying about complex AWS configurations.

Before starting the migration, make sure the following requirements are met:

  • Railway Account: You must have an active Railway account.
  • AWS Admin Access: Ensure you have administrative access to your AWS Management Console.
  • Access to AWS Lambda Functions: Ensure you have access to your AWS Lambda function code, whether from the AWS Management Console or your local machine.
  • Identify Dependencies: List all external resources such as environment variables, and APIs your Lambda functions rely on and add them to Variables in your Railway dashboard.
  1. Download and open the Function code
  2. Package the Function as a Container Image
  3. Deploy the Container Image to Railway
  4. Test the Deployment

Download the function code and open it in your Code Editor

  • Go to the AWS Management Console, navigate to the Lambda dashboard, select your function, and download the code package.
  • Download menu from the Lambda dashboard

    Download menu from the Lambda dashboard

    • Note the Runtime and Architecture your function is built on
    • Unzip and open the code in your preferred Code Editor

    Railway supports several methods of deploying your code, [See]. In this guide, We will deploy our Lambda function to Railway via Docker images directly from Docker Hub.

    You must have Docker (minimum version 20.10.10)

    There are three ways to build a container image for your Lambda function:

    1. Using an AWS base image: Preloaded with runtime.
    2. Using an AWS OS-only base image: For Compiled language.
    3. Using a non-AWS base image: For another Container registry.

    In this guide, we will use the AWS base image.

  • Create a Dockerfile in the same directory as your function. The Dockerfile defines how to build your container image.
  • Expected file structure including Dockerfile

    Expected file structure including Dockerfile

    • Add the following configuration to your Dockerfile:
      • # For NodeJs runtime, and Architecture x86_64
        FROM public.ecr.aws/lambda/nodejs:20-x86_64 
        
        # Copy the function code
        COPY index.mjs ${LAMBDA_TASK_ROOT}
        
        # Set the CMD to your handler (could also be done as a parameter
        # override outside of the Dockerfile)
        CMD [ "index.handler" ]
        # Use the AWS Lambda Node.js base image
        FROM public.ecr.aws/lambda/nodejs:20-x86_64
        
        # Copy files or directories and add to the filesystem of the container at the path.
        COPY ${FILES} ${CONTAINER_PATH}
        
        #RUN npm install to install dependencies
        RUN npm install
        
        # Set the Lambda function handler
        CMD ["index.handler"]
    • Build the Docker image: Open up your terminal and run the following command in the directory containing your Dockerfile
      •  docker build --platform linux/amd64 -t my-railway-function .
        Note: The command specifies the --platform linux/amd64 option to ensure that your container is compatible with the Lambda execution environment regardless of the architecture of your build machine.
    • Test the image locally
      • Start the Docker image with the docker run command.

        docker run --platform linux/amd64 -p 9000:8080 --read-only my-railway-function

        From a new terminal window, post an event to the local endpoint.

        In Linux and macOS, run the following curl command:

        curl "<http://localhost:9000/2015-03-31/functions/function/invocations>" -d '{}'

        If your function code requires a payload, you might want to invoke the function with a JSON payload. Example:

        curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"payload":"hello world!"}'
  • Create an Empty Project in Railway
  • Empty project option from Railway command palette

    Empty project option from Railway command palette

  • Add a new service
  • Add a Service card in empty project

    Add a Service card in empty project

  • Choose Docker Image
  • Docker Image option in Railway command palette

    Docker Image option in Railway command palette

  • Enter name of your image from DockerHub
  • Prompt to enter the image name

    Prompt to enter the image name

  • Deploy your service, and ensure deployment is successful
  • Successful deployment indicator

    Successful deployment indicator

  • Go to Service settings, Under Networking generate a Domain
  • Generating a public domain in the Railway service

    Generating a public domain in the Railway service

  • Enable App Sleep: App Sleep is Railway’s serverless feature that helps reduce service costs by ensuring the application runs only when necessary. It automatically "sleeps" the app during periods of inactivity, and wakes it up when requests are made, minimizing resource usage.
  • App sleeping configuration

    App sleeping configuration

    • Invoke your function: Invoke your function using the generated URL and append /2015-03-31/functions/function/invocations to the endpoint.
      • Example: <generated-url>/2015-03-31/functions/function/invocations

    • View Logs: Railway provides a built-in logging feature to help you diagnose any issues. Check logs for any errors or warnings that may arise during the initial execution.
    • Verify No invocations on AWS: You can view invocations on your function in AWS Management Console to verify no invocations in AWS.
      • Checking for new invocations in AWS Lambda

        Checking for new invocations in AWS Lambda

    • Verify External Connections: If your function depends on external services (like databases or third-party APIs), ensure those connections are working properly in the Railway environment.

    Railway handles scaling automatically, and you do not need to go through the hassle of complex scaling configurations.

    You can use the HTTP logs to monitor your application.

    HTTP Logs tab in Railway

    HTTP Logs tab in Railway

    Railway has a clear and simple pricing model based on usage. You can set limits to ensure you don’t exceed your desired budget.

    • External Resource Management: Ensure that all external resources such as environment variables, and APIs required by your Lambda function are properly noted and included in the container image during packaging.
    • Timeouts or Retries: Railway doesn’t have built-in configurations to manage timeouts or retries. You'll need to handle these manually within your application.

    Migrating from AWS Lambda to Railway offers a more streamlined developer experience, allowing you to focus on building features rather than managing infrastructure. By following these steps—exporting your Lambda functions, package function as a container image, setting up your Railway environment, and deploying your code—you can smoothly transition from AWS Lambda to Railway. With its easy setup, cost-effective solutions, and powerful database integrations, Railway can be an ideal alternative for many serverless projects.