How-to
August 10, 2022

...It Works Fine Locally

Alan Pennell Smith
When you're ready to deploy to production, it can be frustrating to find that your project that worked perfectly on your machine is now throwing errors in the cloud. As you can imagine, the Support Engineering team at Render helps customers fix these issues on a regular basis, so we’ve created a checklist to help prevent them in the first place and save us all some time.

Writing Code

Deploying a project can feel tough, especially when you're starting out. Web development used to be so straightforward. Open up a text editor, write some HTML, add a few images, FTP it all up to a server and you have a website live on the Internet! Simpler times — boring, non-dynamic, table-layout, under-construction-GIF, “best viewed on Netscape Navigator”, hit-counter times, but certainly a lot simpler.
Our vision of a 1990s style Render website. It was a simpler time.
Our vision of a 1990s style Render website. It was a simpler time.
Fast-forward to today, and the number of languages, frameworks, tools, design patterns and a multitude of other aspects surrounding web development are staggering and overwhelming. I've been tinkering on the web since the mid-90s (as you can probably tell from the references above) but the sheer number of choices available to the modern-day web developer makes me wonder how anyone can ever know where to start. In addition to the flood of choices, the pace at which these things evolve can be very hard to keep up with (I'm looking at you, JavaScript!). Luckily, there are also a huge number of helpful resources out there: guides, video courses, the obligatory blog/to-do list tutorials, and our very own Render Quickstarts and Examples. There's also the ever-helpful Stack Overflow when you hit that brick wall of an unexpected error message. Tools like project scaffolding, ORMs, and development servers all make it easier to get started with a new project on your machine. But when it comes to deploying your code, there isn’t enough help online.

Deploying Code

When I made the change in my career from being a web developer (mainly Ruby) to a Support Engineer a few years ago, one phrase quickly became apparent as a common sign-off: "… it works fine locally". Along with its bedfellows "works for me" and "works on my machine", these are phrases we hear often in development, and especially in developer support. Don't get me wrong: it's certainly good to know that there are no glaring code issues preventing a project from spinning up at all. However, the considerations required to deploy a project in the cloud are an essential part of being a web developer these days. Even if it works fine locally, chances are it’s not going to work fine when you put your shiny new project on someone else's machine or platform. We're all keen to get our work out there on the web as soon as possible, but environments will differ — sometimes by a lot. You may develop on Windows or macOS or something more exotic, but almost always, you will deploy to some flavor of Linux. Every language, framework and tool will have a particular version and likely a sprawling list of dependencies (each with its own version, dependency and compatibility requirements) goes all the way down. Again, we're lucky to have some great package management tools to keep all that in check for us. Render Native Environments help the most commonly used project configurations get up and running as quickly as possible, and we inspect your code to supply some defaults along the way. But because every project is different, the required configuration can be too. Here's a checklist that, while focused on Render, will help you regardless of where you're deploying your project.

A Deployment Checklist

This list won't apply to every case, but being mindful of these points may make deploying your application, and debugging any issues with that deployment, a little more tractable.
A checklist for troubleshooting your deployment
A checklist for troubleshooting your deployment
  1. Versioning: Explicitly define versions. Language, package manager and packages. Render has default language versions for native environments, but we'd still recommend you set your own version, e.g. Node, Python, Ruby, etc. This way, you can be sure it's the same as the one you developed on and will continue to stay the same if the default changes in future.
  2. Configuration: Keep configuration in the environment. That may sound familiar if you've ever looked at The Twelve-Factor App. External service URLs, API keys/tokens, SMTP config, etc. will be easier to manage per environment if stored in environment variables or as secret files. And if you need to share these across services, check out Render’s Environment Groups.
  3. Add a Health Check: Create an HTTP endpoint in your app to check if your application is live. Web Services on Render allow you to specify a health check path that's used to check that a new deploy is working as expected before transitioning requests to it. It is also used to check if your app is still up after a deploy; Render automatically restarts your app if the health check endpoint is unresponsive.

    A health check endpoint should touch critical parts of your app, for example, include a simple database connection in it or ping an API endpoint. If your checks pass, return a response code between 200-399 and the deploy will be put live. If the health check responds with a code 400+ the deployment will be marked as a failure and will not proceed.
  4. Production Builds: Verify the dependencies being installed when you build your app in the cloud. Use the same tools you did for development, e.g. npm/Yarn (Node), pip/Poetry (Python), Bundler (Ruby), etc. These tools can also have dependency groups, like devDependencies/dependencies in Node's package.json; make sure your packages are in the right group and be aware that production mode may not install development group packages.I



    Many frameworks also have dev tooling that does things like hot code reloading or just-in-time compilation. The Rails asset pipeline or a Webpack dev server are good examples of this. These development helpers are usually unsuitable for production, so you should build/precompile/transpile as your particular project requires.



    To keep your Render build command(s) tracked along with your code, it can be handy to put it in a shell script and use that to set up the environment. A simple Node based example would be:
    # render-build.sh
    
    #!/usr/bin/env bash
    # exit on error
    set -o errexit
    
    npm install
    npm build
    
  5. Booting your application: Make sure you start your app in production mode and are not using a development server. If you're having trouble with a deploy it can sometimes help to try starting your app in production mode on your dev machine to see if it still works fine locally.



    For Web Services in particular ensure the app server has its host & port set to 0.0.0.0 and the PORT environment variable, respectively. You may also want to consider concurrency settings appropriate for your chosen service plan, e.g. worker counts in Throng (Node), Gunicorn (Python), Puma (Ruby), etc.
  6. File Storage: Service runtime instance filesystems are not persistent. Render service filesystems (and most platforms) are ephemeral, meaning files written after they’re started do not persist after a restart/new deploy. If you need to persist uploaded/generated files between deploys you will need a Render Disk.



    While an ephemeral filesystem may feel counterintuitive at first – "why can't I just save my uploads to my instance?!" – it makes scaling and recovery from outages much more straightforward. Cloud-deployed instances benefit from being readily disposable/recreatable.



    When you do need to store longer-term data, you can add a Render Disk for file persistence. Depending on your data storage needs, it may be also worth considering a dedicated database like PostgreSQL or an object storage service like AWS S3. It’s easy to forget about this during development and end up with your application relying on local filesystem storage.
  7. Custom domains and SSL/TLS: Configure and secure your custom domain. You're unlikely to be running a dev environment on a custom domain or over HTTPS, so you may need to make some configuration updates to ensure your app is aware of how it may receive requests (like ALLOWED_HOSTS in Django, etc.). You may also want to set up a redirect in your code to 301 from the default service-name.onrender.com to your custom domain to keep it canonical.



    SSL/TLS Certificates are fully managed on Render and redirection of HTTP requests to HTTPS is automatic. You may need to ensure your assets use HTTPS in production if using a full URL and not just a relative path, to avoid any mixed-content errors.
With all that said, using Docker can sometimes help reduce the burden of thinking about many of the above items by making your dev environment more closely match the production environment, but using Docker also adds a bunch of additional complexity, and that's a post for another day... We've touched on a lot of areas to get your project deployed successfully, but it’s hard to cover everything in a blog post. If you find you're still having trouble deploying things on Render, we're here to help and you can reach us at support@render.com or post on community.render.com.