With the impending and extremely frustrating slow murder of Heroku I found myself looking for alternative places to run our apps built on the extremely Heroku-friendly PHP8 + PosrgreSQL.
The key requirements for me are:
- Fully managed Postgres
- No fucking about building images, sorry – I want a pre-configured and secure PHP8 environment that’s ready for production
- All managed via the CLI and deployed from git
One that looks promising to me is Upsun, formerly platform.sh. Here’s what I learned from a few hours of cloning an existing app across from Heroku – I make no claims that this is best, or even good, practice. It’s just what I found was needed to get an app deployed for testing.
Key files
All the key configuration of your app is handled via several YAML files. Before anything else, get a decent YAML linter for your IDE or at least stop it from converting spaces to tabs, which will wreck everything.
The config file names seem a bit stuck between the Upsun and Platform branding. But I found that you need, at a minimum:
.platform.app.yaml – in the root
/.platform/routes.yaml
/.platform/services.yaml
Services
In Heroku you would add services via the addons system – so, deploy an app onto X dynos, also provision a Postgres server, link them up. In Upsun you add Postgres as a service in your project. In services.yaml that could be, at a minimum:
postgresql:
type: postgresql:18
disk: 256
Note that this just deploys the service – it does not add a relationship between it and your environment, so the database is just sort of floating there being useless for now. Those links are configured in:
app.yaml
The .platform.app.yaml file sets all kinds of top-level config for your app. Here’s a minimal sample:
name: someapp
type: 'php:8.5'
disk: 2048
web:
locations:
'/':
root: 'web'
passthru: true
index: ["index.php"]
allow: true
relationships:
postgresql:
runtime:
extensions:
- pgsql
Note the top level app name being set, me forwarding all traffic to the /web/ directory as per my old heroku setup, and then two items required to make Postgres work: adding the pgsql extension to PHP and a relationship linking the database to the app. Obviously update the extensions line if you use PDO.
routes.yaml
Add your routes to, appropriately, the routes.yaml file. Here’s an absolutely minimum one, sending any https traffic:
"https://{default}/":
type: upstream
upstream: "someapp:http"
Config variables
There are lots of ways to set config vars, which can be per-project or per-environment. You set them from the CLI:
upsun variable:create --level environment --name VARIABLE_NAME --value VARIABLE_VALUE
The level can be environment or project, which is neat.
DATABASE_URL
This bit proved trickier than I expected. Upsun automatically exposes key variables such as your database credentials, but I found that they needed a bit of rewriting to get them working with my existing code, which expected a Heroku-like DATABASE_URL.
Create this in root as a file called .environment:
export RELATIONSHIPS_JSON="$(echo "$PLATFORM_RELATIONSHIPS" | base64 --decode)"
export DB_CONNECTION="$(echo "$RELATIONSHIPS_JSON" | jq -r '.postgresql[0].scheme')"
export DB_USERNAME="$(echo "$RELATIONSHIPS_JSON" | jq -r '.postgresql[0].username')"
export DB_PASSWORD="$(echo "$RELATIONSHIPS_JSON" | jq -r '.postgresql[0].password')"
export DB_HOST="$(echo "$RELATIONSHIPS_JSON" | jq -r '.postgresql[0].host')"
export DB_PORT="$(echo "$RELATIONSHIPS_JSON" | jq -r '.postgresql[0].port')"
export DB_DATABASE="$(echo "$RELATIONSHIPS_JSON" | jq -r '.postgresql[0].path')"
export DATABASE_URL="postgresql://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_DATABASE}"
.. and you should get a Heroku-code friendly DATABASE_URL. I’m aware that this could be made simpler, but for now it works (hitting an error here? note the postgresql:// bit of the URL, manually added by me).
URL Rewrites
I’m used to Apache’s URL rewriting via .htaccess. Upsun uses nginx, so you need to add rewrites to .platform.app.yaml, where they go under rules within web. An example:
web:
locations:
'/':
root: 'web'
passthru: true
index: ["index.php"]
allow: true
rules:
'^/something/(?<somevar>[^/]+)/$':
passthru: '/somefile.php?somevar=$somevar
That’ll rewrite /something/abc123/ to /somefile.php?somevar=abc123
Based on the above, my test app was live and functional – connected up neatly to Postgres, with API keys properly managed, deployable via git. Adding a custom domain and SSL is simple, and you can open a tunnel to access the database for import.
I’m not yet sure if we’ll be using Upsun going forward, but it certainly looks like a well thought-out option and one that we’ll be testing. If you’re facing the same where-to-go-now dilemma for the coming post-Heroku world, I hope that some of the above might be helpful.