Up Deploy serverless apps in seconds

Introduction

Up deploys infinitely scalable serverless apps, APIs, and static websites in seconds, so you can get back to working on what makes your product unique.

Up focuses on deploying “vanilla” HTTP servers so there’s nothing new to learn, just develop with your favorite existing frameworks such as Express, Koa, Django, Golang net/http or others.

Up currently supports Node.js, Golang, Python, Java, Crystal, and static sites out of the box. Up is platform-agnostic, supporting AWS Lambda and API Gateway as the first targets. You can think of Up as self-hosted Heroku style user experience for a fraction of the price, with the security, flexibility, and scalability of AWS.

Installation

Up is distributed in a binary form and can be installed manually via the tarball releases or one of the options below.

The quickest way to get up is to run the following command, which installs to /usr/local/bin by default.

$ curl -sf https://up.apex.sh/install | sh

NPM’s up package runs the same script as above.

$ npm i -g up

Verify installation with:

$ up version

Later when you want to update up to the latest version use the following command:

$ up upgrade

If you hit permission issues, you may need to run the following, as up is installed to /usr/local/bin/up by default.

$ sudo chown -R $(whoami) /usr/local/bin/

AWS Credentials

Before using Up you need to first provide your AWS account credentials so that resources can be created. There are a number of ways to do that, which are outlined here.

Via environment variables

Using environment variables only, you may specify the following:

  • AWS_ACCESS_KEY_ID AWS account access key
  • AWS_SECRET_ACCESS_KEY AWS account secret key
  • AWS_REGION AWS region

If you have multiple AWS projects you may want to consider using a tool such as direnv to localize and automatically set the variables when you’re working on a project.

Via ~/.aws files

Using the ~/.aws/credentials file to store credentials, allowing you to specify AWS_PROFILE so Up knows which project to reference. To read more on configuring these files view Configuring the AWS CLI.

Here’s an example of ~/.aws/credentials, where export AWS_PROFILE=myapp would activate these settings.

[myapp]
aws_access_key_id = xxxxxxxx
aws_secret_access_key = xxxxxxxxxxxxxxxxxxxxxxxx

Via project configuration

You may store the profile name in the up.json file itself as shown in the following snippet. This is typically ideal since it ensures that you do not accidentally have a different environment set.

{
  "profile": "myapp"
}

IAM Policy for Up CLI

Below is a policy for AWS Identity and Access Management which provides Up access to manage your resources. Note that the policy may change as features are added to Up, so you may have to adjust the policy.

Show policy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "route53:*",
        "route53domains:*"
      ],
      "Effect": "Allow",
      "Resource": "*"
    },
    {
      "Action": [
        "acm:*"
      ],
      "Effect": "Allow",
      "Resource": "*"
    },
    {
      "Action": [
        "s3:*"
      ],
      "Effect": "Allow",
      "Resource": "*"
    },
    {
      "Action": [
        "cloudfront:*"
      ],
      "Effect": "Allow",
      "Resource": "*"
    },
    {
      "Action": [
        "sns:*"
      ],
      "Effect": "Allow",
      "Resource": "*"
    },
    {
      "Action": [
        "ssm:*"
      ],
      "Effect": "Allow",
      "Resource": "*"
    },
    {
      "Action": [
        "cloudformation:Create*",
        "cloudformation:Update*",
        "cloudformation:Delete*",
        "cloudformation:Describe*",
        "cloudformation:ExecuteChangeSet"
      ],
      "Effect": "Allow",
      "Resource": "*"
    },
    {
      "Action": [
        "iam:AttachRolePolicy",
        "iam:CreatePolicy",
        "iam:CreateRole",
        "iam:DeleteRole",
        "iam:DeleteRolePolicy",
        "iam:GetRole",
        "iam:PassRole",
        "iam:PutRolePolicy"
      ],
      "Effect": "Allow",
      "Resource": "*"
    },
    {
      "Action": [
        "lambda:Create*",
        "lambda:Delete*",
        "lambda:Get*",
        "lambda:List*",
        "lambda:Update*",
        "lambda:AddPermission",
        "lambda:RemovePermission",
        "lambda:InvokeFunction"
      ],
      "Effect": "Allow",
      "Resource": "*"
    },
    {
      "Action": [
        "logs:Create*",
        "logs:Put*",
        "logs:Test*",
        "logs:Describe*",
        "logs:FilterLogEvents"
      ],
      "Effect": "Allow",
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "cloudwatch:Get*",
        "cloudwatch:Put*"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "apigateway:*"
      ],
      "Resource": [
        "arn:aws:apigateway:*::/*"
      ]
    }
  ]
}

Getting Started

The simplest Up application is a single file for the application itself, with zero dependencies.

Up runs “vanilla” HTTP servers listening on the PORT environment variable, which is passed to your program by Up. For example create a new directory with the following app.js file:

const http = require('http')
const { PORT = 3000 } = process.env

http.createServer((req, res) => {
  res.end('Hello World from Node.js\n')
}).listen(PORT)

Next you’ll need to let Up know which profile in ~/.aws/credentials to use. Note however that instead of assigning AWS_PROFILE you may use the "profile": "NAME" option in up.json.

$ export AWS_PROFILE=myapp

Deploy it to the development stage:

$ up

Open up the URL in your browser:

$ up url --open

Or copy it to the clipboard:

$ up url --copy

Or test with curl:

$ curl `up url`

That’s it! You’ve deployed a basic Up application. Note that the first deploy may take a minute to set up the resources required. To delete it and its resources, use the following command:

$ up stack delete

If you’re not a Node.js developer here are some examples in additional languages.

For Python create app.py:

from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer
import os

class myHandler(BaseHTTPRequestHandler):
  def do_GET(self):
      self.send_response(200)
      self.send_header('Content-type','text/html')
      self.end_headers()
      self.wfile.write("Hello World from Python\n")
      return

server = HTTPServer(('', int(os.environ['PORT'])), myHandler)
server.serve_forever()

For Golang create main.go:

package main

import (
  "os"
  "fmt"
  "log"
  "net/http"
)

func main() {
  addr := ":"+os.Getenv("PORT")
  http.HandleFunc("/", hello)
  log.Fatal(http.ListenAndServe(addr, nil))
}

func hello(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintln(w, "Hello World from Go")
}

Finally for Crystal create main.cr:

require "http/server"

port = ENV["PORT"].to_i

server = HTTP::Server.new(port) do |ctx|
  ctx.response.content_type = "text/plain"
  ctx.response.print "Hello world from Crystal"
end

server.listen

Configuration

Configuration for your app lives in the up.json within your project’s directory. This section details each of the options available.

Name

The name of the application, which is used to name resources such as the Lambda function or API Gateway.

{
  "name": "api"
}

Profile

The profile property is equivalent to setting AWS_PROFILE for referencing AWS credentials in the ~/.aws directory. Use of this property is preferred it prevents accidents with environment variables.

{
  "profile": "someapp"
}

Regions

You may specify one or more target regions for deployment using the regions array. Glob style patterns may be used to match region ids. By default “us-west-2” is used unless the AWS_REGION environment variable is defined.

A single region:

{
  "regions": ["us-west-2"]
}

Several regions:

{
  "regions": ["us-west-2", "us-east-1", "ca-central-1"]
}

USA and Canada only:

{
  "regions": ["us-*", "ca-*"]
}

Western USA only:

{
  "regions": ["us-west-*"]
}

All regions like a boss:

{
  "regions": ["*"]
}

Currently Lambda supports the following regions:

  • us-east-2 – US East (Ohio)
  • us-east-1 – US East (N. Virginia)
  • us-west-1 – US West (N. California)
  • us-west-2 – US West (Oregon)
  • ap-northeast-2 – Asia Pacific (Seoul)
  • ap-south-1 – Asia Pacific (Mumbai)
  • ap-southeast-1 – Asia Pacific (Singapore)
  • ap-southeast-2 – Asia Pacific (Sydney)
  • ap-northeast-1 – Asia Pacific (Tokyo)
  • ca-central-1 – Canada (Central)
  • eu-central-1 – EU (Frankfurt)
  • eu-west-1 – EU (Ireland)
  • eu-west-2 – EU (London)
  • sa-east-1 – South America (São Paulo)

WARNING: multi-region support won’t be complete until https://github.com/apex/up/issues/134 is closed.

Lambda Settings

The following Lambda-specific settings are available:

  • role – IAM role ARN, defaulting to the one Up creates for you
  • memory – Function memory in mb (Default 128, Min 128, Max 1536)

For example:

{
  "name": "api",
  "lambda": {
    "memory": 512
  }
}

View the Lambda Pricing page for more information regarding the memory setting.

Lambda timeout is implied from the Reverse Proxy timeout setting.

Hook Scripts

Up provides “hooks” which are commands invoked at certain points within the deployment workflow for automating builds, linting and so on. The following hooks are available:

  • prebuild – Run before building
  • build – Run before building. Overrides inferred build command(s)
  • postbuild – Run after building
  • predeploy – Run before deploying
  • postdeploy – Run after deploying
  • clean – Run after a deploy to clean up artifacts. Overrides inferred clean command(s)

Here’s an example using Browserify to bundle a Node application. Use the -v verbose log flag to see how long each hook takes.

{
  "name": "app",
  "hooks": {
    "build": "browserify --node app.js > server.js",
    "clean": "rm server.js"
  }
}

Up performs runtime inference to discover what kind of application you’re using, and does its best to provide helpful defaults – see the (Runtimes)(#runtimes) section.

Multiple commands be provided by using arrays, and are run in separate shells:

{
  "name": "app",
  "hooks": {
    "build": [
      "mkdir -p build",
      "cp -fr static build",
      "browserify --node index.js > build/client.js"
    ],
    "clean": "rm -fr build"
  }
}

To get a better idea of when hooks run, and how long the command(s) take, you may want to deploy with -v for verbose debug logs.

Static File Serving

Up ships with a robust static file server, to enable it specify the app type as "static".

{
  "type": "static"
}

By default the current directory (.) is served, however you can change this using the dir setting. The following configuration restricts only serving of files in ./public/*, any attempts to read files from outside of this root directory will fail.

{
  "name": "app",
  "type": "static",
  "static": {
    "dir": "public"
  }
}

Note that static.dir only tells Up which directory to serve – it does not exclude other files from the directory – see Ignoring Files.

Environment Variables

The environment object may be used for plain-text environment variables. Note that these are not encrypted, and are stored in up.json which is typically committed to GIT, so do not store secrets here.

{
  "name": "api",
  "environment": {
    "API_FEATURE_FOO": "1",
    "API_FEATURE_BAR": "0"
  }
}

These become available to you via process.env.API_FEATURES_FOO, os.Getenv("API_FEATURES_FOO") or similar in your language of choice.

The following environment variables are provided by Up:

  • PORT – port number such as “3000”
  • UP_STAGE – stage name such as “development” or “production”

Up Pro offers encrypted environment variables via the up env sub-command.

Header Injection

The headers object allows you to map HTTP header fields to paths. The most specific pattern takes precedence.

Here’s an example of two header fields specified for /* and /*.css:

{
  "name": "app",
  "type": "static",
  "headers": {
    "/*": {
      "X-Something": "I am applied to everything"
    },
    "/*.css": {
      "X-Something-Else": "I am applied to styles"
    }
  }
}

Requesting GET / will match the first pattern, injecting X-Something:

HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Length: 200
Content-Type: text/html; charset=utf-8
Last-Modified: Fri, 21 Jul 2017 20:42:51 GMT
X-Powered-By: up
X-Something: I am applied to everything
Date: Mon, 31 Jul 2017 20:49:33 GMT

Requesting GET /style.css will match the second, more specific pattern, injecting X-Something-Else:

HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Length: 50
Content-Type: text/css; charset=utf-8
Last-Modified: Fri, 21 Jul 2017 20:42:51 GMT
X-Powered-By: up
X-Something-Else: I am applied to styles
Date: Mon, 31 Jul 2017 20:49:35 GMT

Error Pages

By default Up will serve a minimalistic error page for you if the client accepts text/html, and it is explicitly enabled.

You may customize the default template’s color and optionally provide a support_email to allow customers to contact your support team.

{
  "name": "site",
  "type": "static",
  "error_pages": {
    "enable": true,
    "variables": {
      "support_email": "support@apex.sh",
      "color": "#228ae6"
    }
  }
}

If you’d like to provide custom templates you may create one or more of the following files. The most specific file takes precedence.

  • error.html – Matches any 4xx or 5xx
  • 5xx.html – Matches any 5xx error
  • 4xx.html – Matches any 4xx error
  • CODE.html – Matches a specific code such as 404.html

Variables specified via variables, as well as .StatusText and .StatusCode may be used in the template.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>{{.StatusText}} - {{.StatusCode}}</title>
    <link rel="stylesheet" href="/css/style.css">
  </head>
  <body>
    <h1>{{.StatusText}}</h1>
    {{with .Variables.support_email}}
      <span class="message">Please try your request again or <a href="mailto:{{.}}">contact support</a>.</span>
    {{else}}
      <span class="message">Please try your request again or contact support.</span>
    {{end}}
  </body>
</html>

Script Injection

Scripts, styles, and other tags may be injected to HTML pages before the closing </head> tag or closing </body> tag.

In the following example the <link rel="/style.css"> is injected to the head, as well as the inlining the scripts/config.js file. A <script src="/app.js"></script> is then injected into the body.

{
  "name": "site",
  "type": "static",
  "inject": {
    "head": [
      {
        "type": "style",
        "value": "/style.css"
      },
      {
        "type": "inline script",
        "file": "scripts/config.js"
      }
    ],
    "body": [
      {
        "type": "script",
        "value": "/app.js"
      }
    ]
  }
}

Currently you may specify the following types:

  • literal – A literal string
  • comment – An html comment
  • style – A style href
  • script – A script src
  • inline style – An inline style
  • inline script – An inline script
  • google analytics – Google Analytics snippet with API key
  • segment – Segment snippet with API key

All of these require a value, which sets the src, href, or inline content. Optionally you can populate value via a file path to a local file on disk, this is typically more convenient for inline scripts or styles. For example:

  • { "type": "literal", "value": "<meta name=...>" }
  • { "type": "comment", "value": "Just a boring comment" }
  • { "type": "script", "value": "/feedback.js" }
  • { "type": "style", "value": "/feedback.css" }
  • { "type": "inline script", "file": "/feedback.js" }
  • { "type": "inline style", "file": "/feedback.css" }
  • { "type": "script", "value": "var config = {};" }
  • { "type": "google analytics", "value": "API_KEY" }
  • { "type": "segment", "value": "API_KEY" }

Redirects and Rewrites

Up supports redirects and URL rewriting via the redirects object, which maps path patterns to a new location. If status is omitted (or 200) then it is a rewrite, otherwise it is a redirect.

{
  "name": "app",
  "type": "static",
  "redirects": {
    "/blog": {
      "location": "https://blog.apex.sh/",
      "status": 301
    },
    "/docs/:section/guides/:guide": {
      "location": "/help/:section/:guide",
      "status": 302
    },
    "/store/*": {
      "location": "/shop/:splat"
    }
  }
}

In the previous example /blog will redirect to a different site, while /docs/ping/guides/alerting will redirect to /help/ping/alerting. Finally /store/ferrets and nested paths such as /store/ferrets/tobi will redirect to /shop/ferrets/tobi and so on.

A common use-case for rewrites is for SPAs or Single Page Apps, where you want to serve the index.html file regardless of the path. The other common requirement for SPAs is that you of course can serve scripts and styles, so by default if a file is found, it will not be rewritten to location.

{
  "name": "app",
  "type": "static",
  "redirects": {
    "/*": {
      "location": "/",
      "status": 200
    }
  }
}

If you wish to force the rewrite regardless of a file existing, set force to true as shown here:

{
  "name": "app",
  "type": "static",
  "redirects": {
    "/*": {
      "location": "/",
      "status": 200,
      "force": true
    }
  }
}

Note that more specific target paths take precedence over those which are less specific, for example /blog will win over and /*.

Cross-Origin Resource Sharing

CORS is a mechanism which allows requests originating from a different host to make requests to your API. Several options are available to restrict this access, however to enable CORS for everyone all you need is to add:

{
  "cors": {
    "enable": true
  }
}

Suppose you have https://api.myapp.com, you may want to customize cors to allow access only from https://myapp.com:

{
  "cors": {
    "allowed_origins": ["https://myapp.com"],
    "allowed_methods": ["HEAD", "GET", "POST", "PUT", "PATCH", "DELETE"],
    "allowed_headers": ["Content-Type", "Authorization"]
  }
}
  • allowed_origins – A list of origins a cross-domain request can be executed from. Use * to allow any origin, or a wildcard such as http://*.domain.com. Default is ["*"].
  • allowed_methods – A list of methods the client is allowed to use with cross-domain requests. Default value is simple methods (HEAD, GET, POST).
  • allowed_headers – A list of headers the client is allowed to use with cross-domain requests. If the special * value is present in the list, all headers will be allowed. Default is [].
  • exposed_headers – A list of headers which are safe to expose to the API of a CORS response.
  • max_age – A number indicating how long (in seconds) the results of a preflight request can be cached.
  • allow_credentials – A boolean indicating whether the request can include user credentials such as cookies, HTTP authentication or client side SSL certificates. Defaults to true.

Reverse Proxy

Up acts as a reverse proxy in front of your server, this is how CORS, redirection, script injection and other middleware style features are provided.

The following settings are available:

  • command – Command run through the shell to start your server (Default ./server)
    • When package.json is detected npm start is used
    • When app.js is detected node app.js is used
    • When app.py is detected python app.py is used
  • backoff – Backoff configuration object described in “Crash Recovery”
  • retry – Retry idempotent requests upon 5xx or server crashes. (Default true)
  • timeout – Timeout in seconds per request (Default 15, Max 25)
  • listen_timeout – Timeout in seconds Up will wait for your app to boot and listen on PORT (Default 15, Max 25)
  • shutdown_timeout – Timeout in seconds Up will wait after sending a SIGINT to your server, before sending a SIGKILL (Default 15)
{
  "proxy": {
    "command": "node app.js",
    "timeout": 10,
    "listen_timeout": 5,
    "shutdown_timeout": 5
  }
}

Lambda’s function timeout is implied from the .proxy.timeout setting.

Crash Recovery

Another benefit of using Up as a reverse proxy is performing crash recovery. Up will retry idempotent requests upon failure, and upon crash it will restart your server and re-attempt before responding to the client.

By default the back-off is configured as:

  • min – Minimum time before retrying (Default 100ms)
  • max – Maximum time before retrying (Default 500ms)
  • factor – Factor applied to each attempt (Default 2)
  • attempts – Attempts made before failing (Default 3)
  • jitter – Apply jitter (Default false)

A total of 3 consecutive attempts will be made before responding with an error, in the default case this will be a total of 700ms for the three attempts.

Here’s an example tweaking the default behaviour:

{
  "proxy": {
    "command": "node app.js",
    "backoff": {
      "min": 500,
      "max": 1500,
      "factor": 1.5,
      "attempts": 5,
      "jitter": true
    }
  }
}

Since Up’s purpose is to proxy your http traffic, Up will treat network errors as a crash. When Up detects this, it will allow the server to cleanly close by sending a SIGINT, it the server does not close within proxy.shutdown_timeout seconds, it will forcibly close it with a SIGKILL.

DNS Zones & Records

Up allows you to configure DNS zones and records. One or more zones may be provided as keys in the dns object (“myapp.com” here), with a number of records defined within it.

{
  "name": "gh-polls",
  "dns": {
    "gh-polls.com": [
      {
        "name": "app.gh-polls.com",
        "type": "CNAME",
        "value": ["gh-polls.netlify.com"]
      }
    ]
  }
}

The record type must be one of:

  • A
  • AAAA
  • CNAME
  • MX
  • NAPTR
  • NS
  • PTR
  • SOA
  • SPF
  • SRV
  • TXT

Stages & Custom Domains

Up supports per-stage configuration, such as mapping of custom domains.

By defining a stage and its domain, Up knows it will need to create a free SSL certificate for gh-polls.com, setup the DNS records, and map the domain to API Gateway.

{
  "stages": {
    "production": {
      "domain": "gh-polls.com"
    }
  }
}

Here’s another example mapping each stage to a domain:

{
  "stages": {
    "production": {
      "domain": "api.gh-polls.com"
    },
    "staging": {
      "domain": "stage.gh-polls.com"
    },
    "development": {
      "domain": "dev.gh-polls.com"
    }
  }
}

You may also provide an optional base path, for example to prefix your API with /v1. Note that currently your application will still receive “/v1” in its request path, for example Node’s req.url will be “/v1/users” instead of “/users”.

{
  "stages": {
    "production": {
      "domain": "api.gh-polls.com",
      "path": "/v1"
    }
  }
}

Plan the changes via up stack plan and up stack apply to perform the changes. Note that CloudFront can take up to ~40 minutes to distribute this configuration globally, so grab a coffee while these changes are applied.

Custom stages may be supported in the future, for now there are three:

  • development – The latest deployment
  • staging – The latest deployment specific to “staging”
  • production – The latest deployment specific to “production”

If you’re familiar with AWS Lambda, “development” is the $LATEST version, while “staging” and “production” are aliases.

You may purchase domains from the command-line, or map custom domains from other registrars. Up uses Route53 to purchase domains using your AWS account credit card. See up help domains.

Ignoring Files

Up supports gitignore style pattern matching for omitting files from deployment. The following files are loaded in sequence:

  • .gitignore
  • .upignore

An example .upignore to omit markdown and .go source files might look like this:

*.md
*.go

Negation

By default dotfiles are ignored, if you wish to include them, you may use ! to negate a pattern in .upignore:

!.myfile

Another use-case for negation is to ignore everything and explicitly include a number of files instead, to be more specific:

*
!app.js
!package.json
!node_modules/**
!src/**

Inspecting

To get a better idea of which files are being filtered or added, use up -v when deploying, and you may also find it useful to grep in some cases:

$ up -v 2>&1 | grep filtered
DEBU filtered .babelrc – 25
DEBU filtered .git – 408
DEBU filtered .gitignore – 13
DEBU filtered node_modules/ansi-regex/readme.md – 1749
DEBU filtered node_modules/ansi-styles/readme.md – 1448
DEBU filtered node_modules/binary-extensions/readme.md – 751
DEBU filtered node_modules/chalk/readme.md – 6136

You may also wish to use up build --size to view the largest files within the zip.

Node specifics

When building a Node project, node_modules is often added to .gitignore, so Up special-cases this scenario so that node_modules is not excluded by default.

If you’re using a bundler such as Webpack or Browserify instead, you may wish to ignore node_modules as they’re not required in production, add this to your ./.upignore:

node_modules

Pattern matching

Note that patterns are matched much like .gitignore, so if you have the following .upignore contents even node_modules/debug/src/index.js will be ignored since it contains src.

src

You can be more specific with a leading ./:

./src

Files can be matched recursively using **, for example ignoring everything except the files in dist:

*
!dist/**

Alerting

The Pro version of Up supports defining alerts which can notify your team when your service is failing, responding slowly, or receiving traffic spikes.

{
  "name": "app",
  "actions": [
    {
      "name": "email.backend",
      "type": "email",
      "emails": ["tj@apex.sh"]
    },
    {
      "name": "text.backend",
      "type": "sms",
      "numbers": ["+12508183100"]
    }
  ],
  "alerts": [
    {
      "metric": "http.count",
      "statistic": "sum",
      "threshold": 100,
      "action": "email.backend"
    },
    {
      "metric": "http.5xx",
      "statistic": "sum",
      "threshold": 1,
      "period": "1m",
      "action": "email.backend"
    },
    {
      "metric": "http.4xx",
      "statistic": "sum",
      "threshold": 50,
      "period": "5m",
      "action": "email.backend"
    },
    {
      "metric": "http.latency",
      "statistic": "avg",
      "threshold": 1000,
      "period": "5m",
      "action": "email.backend",
      "description": "Large traffic spike"
    }
  ]
}

Defining Actions

An action must be defined in order to notify your team of triggered and resolved alerts. The action requires a name, which can be any string such as backend, frontend_team, email.backend, email backend – whichever you prefer.

Email

The email action notifies your team via email.

{
  "name": "email.backend",
  "type": "email",
  "emails": ["tj@apex.sh"]
}

SMS

The sms action notifies your team via sms text message.

{
  "name": "text.backend",
  "type": "sms",
  "numbers": ["+12508183100"]
}

Defining Alerts

An alert requires a metric such as request count or latency, statistic such as sum or svg, threshold, and an action to perform when the alert is triggered. Here’s a simple example emailing the backend team when we encounter a spike of over 1000 requests.

{
  "metric": "http.count",
  "statistic": "sum",
  "threshold": 1000,
  "action": "email.backend"
}

Required settings

  • metric – Metric to alert against
    • http.count – Request count
    • http.latency – Request latency in milliseconds
    • http.4xx – HTTP 4xx client errors
    • http.5xx – HTTP 5xx server errors
  • statistic – Statistic name (“sum”, “min”, “max”, “avg”, “count”)
  • threshold – Threshold which is compared to operator
  • action – Name of the action to perform

Optional settings

  • period – Period is the alert query time-span (default: 1m)
  • evaluation_periods – Number of periods to evaluate over (default: 1)
  • operator – Operator is the comparison operator (default >)
  • namespace – Metric namespace (example: “AWS/ApiGateway”)
  • missing– How to treat missing data (default: missing)
  • disable – Disable or mute the alert
  • description – Description of the alert

Runtimes

Up supports a number of interpreted languages, and virtually any language which can be compiled to a binary such as Golang. Up does its best to provide idiomatic and useful out-of-the-box experiences tailored to each language. Currently first-class support is provided for:

  • Python
  • Golang
  • Node.js
  • Crystal
  • Java
  • Static sites

Node.js

When a package.json file is detected, Node.js is the assumed runtime. By default the latest version supported by Lambda is used (6.10). If you’d like to deploy other versions of Node you may want to consider using the Node binaries.

The build hook becomes:

$ npm run build

The server run by the proxy becomes:

$ npm start

Python

When requirements.txt is present the build command becomes:

$ mkdir -p .pypath/ && pip install -r requirements.txt -t .pypath/

The server run by the proxy becomes:

$ python app.py

Golang

When a main.go file is detected, Golang is the assumed runtime.

The build hook becomes:

$ GOOS=linux GOARCH=amd64 go build -o server *.go

The clean hook becomes:

$ rm server

Crystal

When a main.cr file is detected, Crystal is the assumed runtime. Note that this runtime requires Docker to be installed.

The build hook becomes:

$ docker run --rm -v $(PWD):/src -w /src tjholowaychuk/up-crystal crystal build --link-flags -static -o server main.cr

The clean hook becomes:

$ rm server

Java

When a build.gradle file is detected, Gradle is assumed, otherwise if pom.xml is found then Maven is used.

Static

When an index.html file is detected the project is assumed to be static.

Commands

Up provides the up command-line program, used to deploy the app, and manage associated resources such as domains and SSL certificates, as well as operational tasks like viewing logs.

To view details for a command at any time use up help or up help <command>.

Usage:

  up [<flags>] <command> [<args> ...]

Flags:

  -h, --help           Output usage information.
  -r, --region=REGION  Override the region.
  -C, --chdir="."      Change working directory.
  -v, --verbose        Enable verbose log output.
      --version        Show application version.

Commands:

  help                 Show help for a command.
  account status       Status of your account.
  account login        Sign in to your account.
  account logout       Sign out of your account.
  account cards add    Add credit card.
  account cards rm     Remove credit card.
  account cards ls     List credit cards.
  account subscribe    Subscribe to the Pro plan.
  account unsubscribe  Unsubscribe from the Pro plan.
  build                Build zip file.
  config               Show configuration after defaults and validation.
  deploy               Deploy the project.
  domains ls           List purchased domains.
  domains check        Check availability of a domain.
  domains buy          Purchase a domain.
  env ls               List variables.
  env add              Add a variable.
  env rm               Remove a variable.
  logs                 Show log output.
  metrics              Show project metrics.
  run                  Run a hook.
  stack plan           Plan configuration changes.
  stack apply          Apply configuration changes.
  stack delete         Delete configured resources.
  stack status         Show status of resources.
  start                Start development server.
  upgrade              Install the latest release of Up.
  url                  Show, open, or copy a stage endpoint.
  version              Show version.

Deploy

Deploy the project, by default to the “development” stage. Note that running up and up deploy are identical, however for staging and production you must run up deploy <stage>.

Usage:

  up deploy [<stage>]

Flags:

  -h, --help           Output usage information.
  -r, --region=REGION  Override the region.
  -C, --chdir="."      Change working directory.
  -v, --verbose        Enable verbose log output.
      --version        Show application version.

Args:

  [<stage>]  Target stage name.

Examples

Deploy the project to the development stage.

$ up

Deploy the project to the development stage, this is the same as running up without arguments.

$ up deploy

Deploy the project to the staging stage.

$ up deploy staging

Deploy the project to the production stage.

$ up deploy production

Config

Validate and output configuration with defaults applied.

$ up config
{
  "name": "app",
  "description": "",
  "type": "server",
  "headers": null,
  "redirects": null,
  "hooks": {
    "build": "GOOS=linux GOARCH=amd64 go build -o server *.go",
    "clean": "rm server"
  },
  "environment": null,
  "regions": [
    "us-west-2"
  ],
  "inject": null,
  "lambda": {
    "role": "arn:aws:iam::ACCOUNT:role/lambda_function",
    "memory": 128,
    "timeout": 5
  },
  "cors": null,
  "error_pages": {
    "dir": ".",
    "variables": null
  },
  "proxy": {
    "command": "./server",
    "backoff": {
      "min": 100,
      "max": 500,
      "factor": 2,
      "attempts": 3,
      "jitter": false
    }
  },
  "static": {
    "dir": "."
  },
  "logs": {
    "disable": false
  },
  "dns": {
    "zones": null
  }
}
...

Logs

Show or tail log output with optional query for filtering. When viewing or tailing logs, you are viewing them from all stages, see the examples below to filter on a stage name.

Usage:

  up logs [<flags>] [<query>]

Flags:

  -h, --help           Output usage information.
  -r, --region=REGION  Override the region.
  -C, --chdir="."      Change working directory.
  -v, --verbose        Enable verbose log output.
      --version        Show application version.
  -f, --follow         Follow or tail the live logs.
  -s, --since="1d"     Show logs since duration (30s, 5m, 2h, 1h30m, 3d, 1M).
  -e, --expand         Show expanded logs.

Args:

  [<query>]  Query pattern for filtering logs.

Expanded Output

Use the -e or --expand flag to expand log fields:

$ up -e 'path = "/static/*"'

1:36:34pm INFO request
           id: 8ff53267-c33a-11e7-9685-15d48d102ae9
           ip: 70.66.179.182
       method: GET
         path: /static/3.jpg
        stage: development
      version: $LATEST

1:36:34pm INFO response
     duration: 1ms
           id: 8ff53267-c33a-11e7-9685-15d48d102ae9
           ip: 70.66.179.182
       method: GET
         path: /static/3.jpg
         size: 0 B
        stage: development
       status: 304
      version: $LATEST

1:36:34pm INFO request
           id: 8ff4bd57-c33a-11e7-bf4b-4f0d97c427c5
           ip: 70.66.179.182
       method: GET
         path: /static/1.png
        stage: development
      version: $LATEST

JSON Output

When stdout is not a terminal Up will output the logs as JSON, which can be useful for further processing with tools such as jq.

In this contrived example the last 5 hours of production errors are piped to jq to produce a CSV of HTTP methods to IP address.

$ up logs -s 5h 'production error' | jq -r '.|[.fields.method,.fields.ip]|@csv'

Yielding:

"GET","207.194.34.24"
"GET","207.194.34.24"
"GET","207.194.34.24"

Examples

Show logs from the past day.

$ up logs

Show logs from the past 45 minutes.

$ up -s 45m logs

Show logs from the past 12 hours.

$ up -s 12h logs

Show live log output.

$ up logs -f

Show live logs from production only.

$ up logs -f production

Show live error logs from production only.

$ up logs -f 'production error'

Show error logs, which include 5xx responses.

$ up logs error

Show error and warning logs, which include 4xx and 5xx responses.

$ up logs 'warn or error'

Show logs with a specific message.

$ up logs 'message = "user login" method = "GET"'

Show logs with a specific message with implicit =:

$ up logs '"user login" method = "GET"'

Show responses with latency above 15ms.

$ up logs 'duration > 15'

Show 4xx and 5xx responses in production

$ up logs 'production (warn or error)'

Show production 5xx responses with a POST, PUT, or DELETE method.

$ up logs 'production error method in ("POST", "PUT", "DELETE")

Show 200 responses with latency above 1500ms.

$ up logs 'status = 200 duration > 1.5s'

Show responses with bodies larger than 100kb.

$ up logs 'size > 100kb'

Show 4xx and 5xx responses.

$ up logs 'status >= 400'

Show emails containing @apex.sh.

$ up logs 'user.email contains "@apex.sh"'

Show emails ending with @apex.sh.

$ up logs 'user.email = "*@apex.sh"'

Show emails starting with tj@.

$ up logs 'user.email = "tj@*"'

Show logs with a more complex query.

$ up logs 'method in ("POST", "PUT") ip = "207.*" status = 200 duration >= 50'

URL

Show, open, or copy a stage endpoint.

Usage:

  up url [<flags>] [<stage>]

Flags:

  -h, --help           Output usage information.
  -r, --region=REGION  Override the region.
  -C, --chdir="."      Change working directory.
  -v, --verbose        Enable verbose log output.
      --version        Show application version.
  -o, --open           Open endpoint in the browser.
  -c, --copy           Copy endpoint to the clipboard.

Args:

  [<stage>]  Name of the stage.

Examples

Show the development endpoint.

$ up url

Open the development endpoint in the browser.

$ up url --open

Copy the development endpoint to the clipboard.

$ up url --copy

Show the production endpoint.

$ up url production

Open the production endpoint in the browser.

$ up url -o production

Copy the production endpoint to the clipboard.

$ up url -c production

Metrics

Show project metrics and estimated cost breakdown for requests, invocation count and the time spent for Lambda invocations.

Usage:

  up metrics [<flags>] [<stage>]

Flags:

  -h, --help           Output usage information.
  -r, --region=REGION  Override the region.
  -C, --chdir="."      Change working directory.
  -v, --verbose        Enable verbose log output.
      --version        Show application version.
  -s, --since="1M"     Show logs since duration (30s, 5m, 2h, 1h30m, 3d, 1M).

Args:

  [<stage>]  Name of the stage.

For example:

$ up metrics production -s 15d

  Requests: 13,653 ($0.01)
  Duration min: 0ms
  Duration avg: 48ms
  Duration max: 15329ms
  Duration sum: 3m6.611s ($0.00)
  Errors 4xx: 1,203
  Errors 5xx: 2
  Invocations: 12,787 ($0.00)
  Errors: 0
  Throttles: 0

Start

Start development server. The development server runs the same proxy that is used in production for serving, so you can test a static site or application locally with the same feature-set.

Currently up start does not work with cross-compiled languages such as Go or Crystal.

Usage:

  up start [<flags>]

Flags:

  -h, --help             Output usage information.
  -r, --region=REGION    Override the region.
  -C, --chdir="."        Change working directory.
  -v, --verbose          Enable verbose log output.
      --version          Show application version.
      --address=":3000"  Address for server.

Examples

Start development server on port 3000.

$ up start

Start development server on port 5000.

$ up start --address :5000

Domains

Manage domain names, and purchase them from AWS Route53 as the registrar.

Usage:

  up domains <command> [<args> ...]

Flags:

  -h, --help           Output usage information.
  -r, --region=REGION  Override the region.
  -C, --chdir="."      Change working directory.
  -v, --verbose        Enable verbose log output.
      --version        Show application version.

Subcommands:

  domains list    List purchased domains.
  domains check   Check availability of a domain.
  domains buy     Purchase a domain.

Examples

List purchased domains.

$ up domains

Check availability of a domain.

$ up domains check example.com

Purchase a domain (with interactive form).

$ up domains buy

Stack

Stack resource management. The stack is essentially all of the resources powering your app, which is configured by Up on the first deploy.

At any time if you’d like to delete the application simply run $ up stack delete. To view the status and potential errors use $ up stack.

Usage:

  up stack <command> [<args> ...]

Flags:

  -h, --help           Output usage information.
  -r, --region=REGION  Override the region.
  -C, --chdir="."      Change working directory.
  -v, --verbose        Enable verbose log output.
      --version        Show application version.

Subcommands:
  stack plan      Plan configuration changes.
  stack apply     Apply configuration changes.
  stack delete    Delete configured resources.
  stack status    Show status of resources.

Examples

Show status of the stack resources and nameservers.

$ up stack

Show resource changes.

$ up stack plan

Apply resource changes.

$ up stack apply

Delete the stack resources.

$ up stack delete

Build

Build zip file, typically only helpful for inspecting its contents. If you’re interested in seeing what files are causing bloat, use the --size flag to list files by size descending.

Usage:

  up build [<flags>]

Flags:

  -h, --help           Output usage information.
  -r, --region=REGION  Override the region.
  -C, --chdir="."      Change working directory.
  -v, --verbose        Enable verbose log output.
      --version        Show application version.
      --size           Show zip contents size information.

Examples

Build archive and save to ./out.zip

$ up build

Build archive and output to file via stdout.

$ up build > /tmp/out.zip

Build archive list files by size.

$ up build --size

Build archive and list size without creating out.zip.

$ up build --size > /dev/null

Env

Manage encrypted environment variables. Environment variables are scoped to all stages by default, with stage-level overrides. The up env command is available in the Pro plan.

Usage:

  up env <command> [<args> ...]

Flags:

  -h, --help           Output usage information.
  -r, --region=REGION  Override the region.
  -C, --chdir="."      Change working directory.
  -v, --verbose        Enable verbose log output.
      --version        Show application version.

Subcommands:

  env ls               List variables.
  env add              Add a variable.
  env rm               Remove a variable.
$ up env add DB_NAME users
$ up env add DB_USER sloth
$ up env add DB_URL stage.mydb.hosted.com
$ up env add DB_PASS passforstage
$ up env add DB_CONN "host=localhost port=5432"

Overriding for a stage such as production can be specified with the -s, --stage flag. For example DB_NAME and DB_USER would likely remain the same in production, however the DB_URL and DB_PASS would not, so we should assign them as shown here:

$ up env add -s production DB_URL prod.mydb.hosted.com
$ up env add -s production DB_PASS passforprod

Environment variables may also be plain or “clear” text, using the -c, --clear flag, which may be viewed in the output.

$ up env add -c DB_NAME users
$ up env add -c DB_USER sloth
$ up env add DB_PASS amazingpass

List the env vars with:

$ up env

all                                                   

DB_NAME  users  -  Modified 34 seconds ago by tobi
DB_PASS  -      -  Modified 22 seconds ago by tobi
DB_URL   -      -  Modified 24 seconds ago by tobi
DB_USER  sloth  -  Modified 30 seconds ago by tobi

production                                            

DB_PASS  -      -  Modified 2 seconds ago by tobi  
DB_URL   -      -  Modified 4 seconds ago by tobi  

Note that you can also assign descriptions with the -d, --desc flag. Note that -cd is equivalent to -c, -d marking the first two variables as cleartext.

$ up env set -cd 'MongoDB collection name' DB_NAME users
$ up env set -cd 'MongoDB name' DB_NAME users
$ up env set -d 'MongoDB address' DB_URL bar
$ up env set -d 'MongoDB password' DB_PASS foo

Check the output and you’ll see the descriptions:

$ up env

all                                                                         

DB_NAME  users  MongoDB collection name  Modified 2 minutes ago by tobi  
DB_PASS  -      MongoDB password         Modified 19 seconds ago by tobi
DB_URL   -      MongoDB address          Modified 1 second ago by tobi   
DB_USER  sloth  MongoDB username         Modified 2 minutes ago by tobi  

Note that while changes made to the variables are effective immediately, AWS Lambda may retain idle containers with the previous values. Currently you must perform a deploy in order to receive the new values. This can be used to your advantage, as it allows you to change for example both DB_USER and DB_PASS at the same time.

Guides

Subscribing to Up Pro

Up Pro provides additional features which are not available in the open-source version, such as encrypted environment variables, alerting support and more.

First sign into the platform with the following command, you’ll receive an email for confirmation.

$ up account login

     email: tj@apex.sh
  ⠋ verify: Check your email for a confirmation link

Click the link in your email and you’re signed in! Next, you’ll need to add a credit card. Note that this information is transmitted to Stripe directly via HTTPS, it never touches the Apex servers.

$ up account cards add

   Name: TJ Holowaychuk
   Number: 5555555555554444
   CVC: 123
   Expiration month: 05
   Expiration year: 2019
   Country: Canada
   City: Victoria
   State: BC
   Address: Your address here
   Zip: V5B 7R2

Then subscribe to Pro!

$ up account subscribe

Once signed-in future you can run up upgrade to install the latest version of Up Pro instead of the open-source distribution:

$ up upgrade

To view the status of your account at any time run the following:

$ up account

  status: Signed in
  active team: tj@apex.sh

  subscription: Up Pro
  amount: $10.00/mo USD
  created: November 12, 2017

To switch to another team run the following and select the active team.

$ up account switch


   ❯ apex
     tj@apex.sh

At any time you can ensure you’re on Up Pro up version:

$ up version
0.1.1-pro

Note that AWS charges for your resource usage, and is not associated with Up Pro’s subscription. Most small to medium applications will fit within AWS’ free tier, however, you should consult AWS’ pricing documentation for details. The Serverless Calc is a useful tool for estimating the API Gateway and Lambda charges.

Inviting Team Members

Once you have subscribed to Up Pro, you may invite team members so they have access to Up Pro as well. By default you have a personal team, the team id is your email address.

Run the following command to create an invite and send an email with installation instructions:

$ up account invite --email tobi@apex.sh

Your co-worker will receive an email with installation instructions. There is currently no limit to the number of team members.

For example, if your email is “tj@apex.sh”, and you invite “tobi@apex.sh”, they will need to run the following to join your personal team.

$ up account login --email tobi@apex.sh --team tj@apex.sh

If your team name is “netflix” they would run:

$ up account login --email tobi@apex.sh --team netflix

Development to Production Workflow

This section guides you through taking a small application from development, to production, complete with purchasing and mapping a custom domain.

Deploying

First, create app.js in an empty directory with the following Node.js app. Note that it must listen on PORT which is passed by Up.

const http = require('http')
const { PORT = 3000 } = process.env

http.createServer((req, res) => {
  res.end('Hello World\n')
}).listen(PORT)

Next, you should give your application a name and start configuring. The profile name should correspond to the name in ~/.aws/credentials so that Up knows which AWS account to deploy to, and which credentials to use.

{
  "name": "up-example",
  "profile": "up-tobi"
}

Run up to deploy the application.

$ up

   build: 5 files, 3.9 MB (358ms)
  deploy: complete (14.376s)
   stack: complete (1m12.086s)

Test with curl to ensure everything is working:

$ curl `up url`
Hello World

Purchasing a Domain

Domains can be mapped from existing services, or purchased directly from AWS via Route53. First check if the domain you’d like is available:

$ up domains check up.com

  Domain up.com is unavailable

  Suggestions:

  theupwards.com          $12.00 USD
  upwardonline.com        $12.00 USD
  myupwards.com           $12.00 USD
  theastir.com            $12.00 USD
  astironline.com         $12.00 USD
  myastir.com             $12.00 USD
  myupward.net            $11.00 USD
  cleanup.tv              $32.00 USD
  myup.tv                 $32.00 USD
  itup.tv                 $32.00 USD
  newup.tv                $32.00 USD
  thedown.net             $11.00 USD
  theupward.net           $11.00 USD
  upwardsonline.net       $11.00 USD

Oh no up.com is taken! Try another:

$ up domains check up-example.com

  Domain up-example.com is available for $12.00 USD

Purchase it with the following command and fill out the details required by the registrar:

$ up domains buy

  Domain: up-example.com
  First name: TJ
  Last name: Holowaychuk
  Email: tj@apex.sh
  Phone: +1.2501007000
  Country code: CA
  City: Victoria
  State or province: BC
  Zip code: X9X 9X9
  Address: Some address here

It can take a few minutes for AWS to finalize the purchase after which you should receive an email. Then you’ll see it in the up domains output along with the automatic renewal time.

$ up domains

  gh-polls.com             renews Aug 28 17:17:58
  up-example.com           renews Sep 19 19:40:50

By default domains purchased with Up have privacy protection enabled, hiding your contact information from WHOIS.

Deploying to Stages

Before deploying to the staging and production stages, first tweak the application a little to include the UP_STAGE environment variable:

const http = require('http')
const { PORT = 3000, UP_STAGE } = process.env

http.createServer((req, res) => {
  res.end('Hello World from ' + UP_STAGE)
}).listen(PORT)

Now deploy to development and production. Note that up is an alias of up deploy development.

$ up
$ up deploy production

Open both in the browser:

$ up url -o
$ up url production -o

You should see “Hello World from production” and “Hello World from development”.

Mapping Custom Domains to Stages

Now that you have an application deployed, you probably want a fancy custom domain for it right? You can map these using the stages and domain properties.

Here we let Up know that we want up-example.com for production and dev.up-example for development. You could also map staging to stage.up-example.com or similar if you’d like.

{
  "name": "up-example",
  "profile": "up-tobi",
  "stages": {
    "development": {
      "domain": "dev.up-example.com"
    },
    "production": {
      "domain": "up-example.com"
    }
  }
}

Note that you could map staging to a domain like staging-myapp.com as well, it does not have to be a sub-domain of your production domain.

Now when you run up stack plan to preview changes to your resources, it will prompt you to verify the Let’s Encrypt certificate emails that AWS sends.

$ up stack plan

       domains: Check your email for certificate approval
     ⠧ confirm: up-example.com

AWS requires email verification to prove you own the domain. It will attempt to send an email to all of the following emails:

  • administrator@your_domain_name
  • hostmaster@your_domain_name
  • postmaster@your_domain_name
  • webmaster@your_domain_name
  • admin@your_domain_name

See Validate Domain Ownership for more information.

After clicking “I Approve” in the email, the output will resume and you’ll see some new resources Up will be creating.

Add AWS::ApiGateway::DomainName
  id: ApiDomainDevelopment

Add AWS::ApiGateway::BasePathMapping
  id: ApiDomainDevelopmentPathMapping

Add AWS::ApiGateway::DomainName
  id: ApiDomainProduction

Add AWS::ApiGateway::BasePathMapping
  id: ApiDomainProductionPathMapping

Add AWS::Route53::RecordSet
  id: DnsZoneDevUpExampleComRecordDevUpExampleCom

Add AWS::Route53::RecordSet
  id: DnsZoneUpExampleComRecordUpExampleCom

If you’re curious, now that Up knows you want to map the domain(s), it will create:

  • Registers ACM free SSL certificate(s) for your domain(s)
  • CloudFront distribution for the API Gateway
  • API Gateway stage mapping
  • Route53 DNS zone and record(s) mapping to the CloudFront distribution

Now apply these changes:

$ up stack apply

After the changes have been applied, it can take roughly 10-40 minutes for CloudFront to distribute the configuration and SSL certificate globally, so until then our up-example.com domain won’t work.

Once available https://up-example.com will always point to production via up deploy production, and https://dev.up-example.com/ will point to the latest deployment via up.

Mapping Domains from External Registrars

If you purchased a domain via up domains buy you can skip this step, however if you used an external registrar such as Godaddy you will need to delegate to AWS for DNS management.

To do this you’ll need to sign in to your registrar’s site, and configure the nameservers. To figure out what values to use for the nameservers, run up stack, which outputs the NS records for the apex (top-level) domains of your application.

$ up stack

Staging

  domain: stage.up-example.com
  endpoint: d2od0udp1p8bru.cloudfront.net

Production

  domain: up-example.com
  endpoint: d72wsqljqg5cy.cloudfront.net
  nameservers:
   • ns-1495.awsdns-58.org
   • ns-103.awsdns-12.com
   • ns-1670.awsdns-16.co.uk
   • ns-659.awsdns-18.net

Save those four values in your registrar’s interface, and you should be good to go! Note that altering DNS records can take some time to propagate.

Mapping with Third-party DNS

If you manage DNS with a third-party such as Cloudflare, and wish to use Up only for deployment you will need to manually edit or add DNS records.

For example, if your domain sloths.com is managed by Cloudflare and you’d like point api.sloths.com to your app, you will need to create a CNAME for api.sloths.com pointing to the endpoint for the stage you’d like to map. Use up stack after your app is deployed as shown here to obtain this information.

$ up stack

Staging

  domain: stage.up-example.com
  endpoint: d2od0udp1p8bru.cloudfront.net

Production

  domain: up-example.com
  endpoint: d72wsqljqg5cy.cloudfront.net
  nameservers:
   • ns-1495.awsdns-58.org
   • ns-103.awsdns-12.com
   • ns-1670.awsdns-16.co.uk
   • ns-659.awsdns-18.net

Stack Changes

The “stack” is all of the resources associated with your app. You plan changes via up stack plan and perform them with up stack apply.

Suppose you wanted to map the “staging” stage, you would first add it to up.json:

{
  "name": "up-example",
  "profile": "up-tobi",
  "stages": {
    "development": {
      "domain": "dev.up-example.com"
    },
    "staging": {
      "domain": "stage.up-example.com"
    },
    "production": {
      "domain": "up-example.com"
    }
  }
}

Then run:

$ up stack plan

Review the output, it should be all “Add”s in this case, then apply:

$ up stack apply

Deleting the App

After you’re done messing around, you may want to remove all the resources and the app itself. To do so simply run:

$ up stack delete

Deploying Applications from Continuous Integration

Up makes it easy to deploy your applications from CI, thanks to its Go binaries you can install Up in seconds in any CI provider such as Travis, Circle, Semaphore among others.

Environment Variables

The first step is to set up environment variables so that you have access to your AWS account. You can get these values from cat ~/.aws/credentials:

  • AWS_ACCESS_KEY_ID – AWS access key
  • AWS_SECRET_ACCESS_KEY – AWS secret key

If using running Up Pro you’ll need your Up credentials:

  • UP_CONFIG – Up configuration

To obtain this run up account ci or up account ci --copy to copy it directly to your clipboard, then paste this as the env var’s value.

Commands

Now that configuration is prepared, you’ll need to install Up using the following two commands:

$ sudo chown -R $(whoami) /usr/local/bin
$ curl -sf https://up.apex.sh/install | sh

If you’re on Up Pro you’ll need to upgrade to install the Pro binary:

$ up upgrade

After that, all you need to do is deploy to your desired stage!

$ up deploy production

If your provider sets CI=true colors will be disabled automatically, however, you may manually specify this using --format:

$ up --format=plain deploy production

Mastering Logging

This section describes how you can log from your application in a way that Up will recognize. In the future Up will support forwarding your logs to services such as Loggly, Papertrail or ELK.

Plain Text

The first option is plain-text logs to stdout or stderr. Currently writes to stderr are considered ERROR-level logs, and stdout becomes INFO.

Writing plain-text logs is simple, for example with Node.js:

console.log('User signed in')
console.error('Failed to sign in: %s', err)

Would be collected as:

 INFO: User signed in
ERROR: Failed to sign in: something broke

Multi-line indented logs are also supported, and are treated as a single message. For example:

console.log('User signed in')
console.log('  name: %s', user.name)
console.log('  email: %s', user.email)

Would be collected as the single entry:

INFO: User signed in
  name: tj
  email: tj@apex.sh

This feature is especially useful for stack traces.

JSON

The second option is structured logging with JSON events, which is preferred as it allows you to query against specific fields and treat logs like events.

JSON logs require a level and message field:

console.log(`{ "level": "info", "message": "User login" }`)

Would be collected as:

INFO: User login

The message field should typically contain no dynamic content, such as user names or emails, these can be provided as fields:

console.log(`{ "level": "info", "message": "User login", "fields": { "name": "Tobi", "email": "tobi@apex.sh" } }`)

Would be collected as:

INFO: User login name=Tobi email=tobi@apex.sh

Allowing you to perform queries such as:

$ up logs 'message = "User login" name = "Tobi"'

Or:

$ up logs 'name = "Tobi" or email = "tobi@*"'

Here’s a simple JavaScript logger for reference. All you need to do is output some JSON to stdout and Up will handle the rest!

function log(level, message, fields = {}) {
  const entry = { level, message, fields }
  console.log(JSON.stringify(entry))
}

For example, with the Go apex/log package you’d use the json handler, which outputs this format.

Log Query Language

Up supports a comprehensive query language, allowing you to perform complex filters against structured data, supporting operators, equality, substring tests and so on. This section details the options available when querying.

AND Operator

The and operator is implied, and entirely optional to specify, since this is the common case.

Suppose you have the following example query to show only production errors from a the specified IP address.

production error ip = "207.194.32.30"

The parser will inject and, effectively compiling to:

production and error and ip = "207.194.38.50"

Or Operator

There is of course also an or operator, for example showing warnings or errors.

production (warn or error)

These may of course be nested as you require:

(production or staging) (warn or error) method = "GET"

Equality Operators

The = and != equality operators allow you to filter on the contents of a field.

Here = is used to show only GET requests:

method = "GET"

Or for example != may be used to show anything except GET:

method != "GET"

Relational Operators

The >, >=, <, and <= relational operators are useful for comparing numeric values, for example response status codes:

status >= 200 status < 300

Stages

Currently all development, staging, and production logs are all stored in the same location, however, you may filter to find exactly what you need.

The keywords production, staging, and development expand to:

stage = "production"

For example, filtering on slow production responses:

production duration >= 1s

Is the same as:

stage = "production" duration >= 1s

Severity Levels

Up provides request level logging with severity levels applied automatically. For example, a 5xx response is an ERROR level, while 4xx is a WARN, and 3xx or 2xx are the INFO level.

This means that instead of using the following for showing production errors:

production status >= 500

You may use:

production error

In Operator

The in operator checks for the presence of a field within the set provided. For example, showing only POST, PUT and PATCH requests:

method in ("POST", "PUT", "PATCH")

Units

The log grammar supports units for bytes and durations. For example, showing responses larger than 56kb:

size > 56kb

Or showing responses longer than 1500ms:

duration > 1.5s

Byte units are:

  • b bytes (123b or 123 are equivalent)
  • kb bytes (5kb, 128kb)
  • mb bytes (5mb, 15.5mb)

Duration units are:

  • ms milliseconds (100ms or 100 are equivalent)
  • s seconds (1.5s, 5s)

Substring Matches

When filtering on strings, such as the log message, you may use the * character for substring matches.

For example if you want to show logs with a remote ip prefix of 207.:

ip = "207.*"

Or a message containing the word “login”:

message = "*login*"

There is also a special keyword for this case:

message contains "login"

Local Environment Variables

If you’d like to define custom local development environment variables, we recommend using the direnv tool, which allows you to create a ./.envrc file in your project.

Install it:

$ brew install direnv

Add the following to your bash profile:

eval `direnv hook bash`

Add some env vars:

$ echo "export DB_URL=foo" >> .envrc

GIT ignore them and allow sourcing:

$ echo .envrc >> .gitignore

Allow access now that you’re sure the .envrc file isn’t checked into GIT, these environment variables will automatically be set each time you cd into the project directory.

$ direnv allow
direnv: loading .envrc
direnv: export +DB_URL

FAQ

Is this a hosted service?

There are currently no plans for a hosted version. Up lets you deploy applications to your own AWS account for isolation, security, and longevity, don’t worry about a startup going out of business.

How much does Up Pro cost?

Up’s subscription fee is currently $20/mo USD. When subscribed your team has access to Up Pro updates until the subscription is cancelled. When cancelled you receive access until the end if your billing period.

For the subscription you get unlimited access within your organization, there is no additional fee per team member or “seat”. You can deploy any number of applications as you wish.

Note that AWS charges for use of its resources, however most small to medium applications will fit within the AWS free tier.

What platforms does Up support?

Currently AWS via API Gateway and Lambda are supported, this is the focus until Up is nearing feature completion, after which additional providers such as GCP and Azure will be added.

How is this different than other serverless frameworks?

Most of the AWS Lambda based tools are function-oriented, while Up abstracts this away entirely. Up does not use framework “shims”, the servers that you run using Up are regular HTTP servers and require no code changes for Lambda compatibility.

Up keeps your apps and APIs portable, makes testing them locally easier, and prevents vendor lock-in. The Lambda support for Up is simply an implementation detail, you are not coupled to API Gateway or Lambda. Up uses the API Gateway proxy mode to send all requests (regardless of path or method) to your application.

If you’re looking to manage function-level event processing pipelines, Apex or Serverless are likely better candidates, however if you’re creating applications, apis, micro services, or websites, Up is built for you.

Why run HTTP servers in Lambda?

You might be thinking this defeats the purpose of Lambda, however most people just want to use the tools they know and love. Up lets you be productive developing locally as you normally would, Lambda for hosting is only an implementation detail.

With Up you can use any Python, Node, Go, or Java framework you’d normally use to develop, and deploy with a single command, while maintaining the cost effectiveness, self-healing, and scaling capabilities of Lambda.

How much does it cost to run an application?

AWS API Gateway provides 1 million free requests per month, so there’s a good chance you won’t have to pay anything at all. Beyond that view the AWS Pricing for more information.

How well does it scale?

Up scales to fit your traffic on-demand, you don’t have to do anything beyond deploying your code. There’s no restriction on the number of concurrent instances, apps, custom domains and so on.

How much latency does Up’s reverse proxy introduce?

With a 512mb Lambda function Up introduces an average of around 500µs (microseconds) per request.

Can I remove the /development and /production paths?

Up uses AWS API Gateway, which imposes the stage base paths. Currently there is no way to remove them, however when you use custom domains these paths are not present.

Do the servers stay active while idle?

This depends on the platform, and with Lambda being the initial platform provided the current answer is no, the server(s) are frozen when inactive and are otherwise “stateless”.

Typically relying on background work in-process is an anti-pattern as it does not scale. Lambda functions combined with CloudWatch scheduled events for example are a good way to handle this kind of work, if you’re looking for a scalable alternative.

What databases can I use?

You’re not limited to databases from any given platform, such as AWS Provided that the database host provides authentication, you can use anything. See the Wiki for a list of managed & serverless database solutions.

Why is Up licensed as GPLv3?

Up is licensed in such a way that myself as an independent developer can continue to improve the product and provide support. Commercial customers receive access to a premium version of Up with additional features, priority support for bugfixes, and of course knowing that the project will stick around! Up saves your team countless hours maintaining infrastructure and custom tooling, so you can get back to what makes your company and products unique.

Can I donate?

Yes you can! Head over to the OpenCollective page. Any donations are greatly appreciated and help me focus more on Up’s implementation, documentation, and examples. If you’re using the free OSS version for personal or commercial use please consider giving back, even a few bucks buys a coffee :).