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 — just $ up
and you’re done!
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
To install up
to a specific directory, use BINDIR
. Here’s an example installing to the current directory:
$ curl -sf https://up.apex.sh/install | BINDIR=. sh
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/
Before using Up you need to first provide your AWS account credentials so that Up is allowed to create resources on your behalf.
Most AWS tools support the ~/.aws/credentials
file for storing credentials, allowing you to specify AWS_PROFILE
environment variable so Up knows which one 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=myaccount
would activate these settings.
[myaccount] aws_access_key_id = xxxxxxxx aws_secret_access_key = xxxxxxxxxxxxxxxxxxxxxxxx
You may store the profile name in the up.json
file itself as shown in the following snippet:
{ "name": "appname-api", "profile": "myaccount" }
This is ideal as it ensures you will not accidentally deploy to a different AWS account.
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.
If you’re using Up for a production application it’s highly recommended to configure an IAM role and user(s) for your team, restricting the access to the account and its resources.
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "acm:*", "cloudformation:Create*", "cloudformation:Delete*", "cloudformation:Describe*", "cloudformation:ExecuteChangeSet", "cloudformation:Update*", "cloudfront:*", "cloudwatch:*", "ec2:*", "ecs:*", "events:*", "iam:AttachRolePolicy", "iam:CreatePolicy", "iam:CreateRole", "iam:DeleteRole", "iam:DeleteRolePolicy", "iam:GetRole", "iam:PassRole", "iam:PutRolePolicy", "lambda:AddPermission", "lambda:Create*", "lambda:Delete*", "lambda:Get*", "lambda:InvokeFunction", "lambda:List*", "lambda:RemovePermission", "lambda:Update*", "logs:Create*", "logs:Describe*", "logs:FilterLogEvents", "logs:Put*", "logs:Test*", "route53:*", "route53domains:*", "s3:*", "ssm:*", "sns:*" ], "Resource": "*" }, { "Effect": "Allow", "Action": "apigateway:*", "Resource": "arn:aws:apigateway:*::/*" } ] }
The simplest Up application is a single file for the application itself, with zero dependencies, and an up.json
file which requires only a name
.
If the directory does not contain an up.json
file, the first execution of up
will prompt you to create it, or you can manually create an up.json
with some preferences:
{ "name": "appname-api", "profile": "companyname", "regions": ["us-west-2"] }
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)
Deploy it to the staging environment:
$ up
Open up the URL in your browser:
$ up url --open
Or test with curl:
$ curl `up url`
That’s it! You’ve deployed a basic Up application. To view further help for commands use:
$ up help $ up help COMMAND $ up help COMMAND SUBCOMMAND
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 for your app lives in the up.json
within your project’s directory. This section details each of the options available.
The name of the application, which is used to name resources such as the Lambda function or API Gateway.
{ "name": "api" }
The profile
property is equivalent to setting AWS_PROFILE
for referencing AWS credentials in the ~/.aws
directory. Use of this property is preferred as it prevents accidents with environment variables.
{ "profile": "someapp" }
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.
Currently only a single region is supported until the issue https://github.com/apex/up/issues/134 is closed.
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:
The following Lambda-specific settings are available:
role
– IAM role ARN, defaulting to the one Up creates for youmemory
– Function memory in mb (Default 512
, Min 128
, Max 3008
)policy
– IAM function policy statement(s)vpc
- VPC subnets and security groupsaccelerate
– Enable S3 Transfer Acceleration (Default false
)layers
— AWS Lambda Layers to use in the function (Up Pro only)For example:
{ "name": "api", "lambda": { "memory": 512, "vpc": { "subnets": [ "subnet-aaaaaaa", "subnet-bbbbbbb", "subnet-ccccccc", ], "security_groups": [ "sg-xxxxxxx" ] }, "layers": [ "arn:aws:lambda:us-west-2:337324593253:layer:nodejs-chrome:1" ] } }
The Lambda memory
setting also scales the CPU, if your app is slow, or for cases such as larger Node applications with many require()
s you may need to increase this value. View the Lambda Pricing page for more information regarding the memory
setting.
Changes to Lambda configuration do not require a up stack apply
, just deploy and these changes are picked up!
AWS Lambda Layers allow you to store binaries and app dependencies such as node_modules in a common layer which is pulled in to your app’s function.
For example the headless Chrome and Puppeteer dependencies add roughly 35mb to your deployment, these can be stored in a layer and used across any number of Up applications, easing dependency management and speeding up deploys.
$ mkdir nodejs $ npm install chrome-aws-lambda puppeteer-core $ mv node_modules nodejs $ zip -r function.zip . $ aws lambda publish-layer-version \ --layer-name nodejs-chrome \ --description "Node.js Headless Chrome" \ --license-info MIT \ --zip-file fileb://function.zip \ --compatible-runtimes nodejs6.10 nodejs8.10
Up uses IAM policies to grant access to resources within your AWS account such as DynamoDB or S3.
To add additional permissions add one or more IAM policy statements to the policy
array, in the following example we permit DynamoDB item reading, updating, and deleting.
{ "name": "myapp", "lambda": { "memory": 1024, "policy": [ { "Effect": "Allow", "Resource": "*", "Action": [ "dynamodb:Get*", "dynamodb:List*", "dynamodb:PutItem", "dynamodb:DeleteItem" ] } ] } }
Deploy to update the IAM function role permissions.
Up Pro supports active warming to mitigate cold starts. A “cold start” occurs in AWS Lambda when there are no idle containers available to serve a request—Lambda must fetch your code and create a new container, after this it is “warm” and remains in the Lambda cluster to serve subsequent requests for roughly an hour.
If a container does not receive any traffic within the hour, it is removed from the AWS Lambda cluster, and thus a new request may incur a cold start. Up Pro’s “active warming” feature mitigates this by periodically requesting against your app, at the specified concurrency. It tries to maintain at least warm_count
idle containers.
For example when a user visits your web application, a cold start may occur for each resource, say you have one JavaScript file, CSS file, and the HTML itself, then this will be 3 concurrent containers. By default Up will warm 15
containers, however you may want to adjust warm_count
this for your use-case.
Note that if your application receives steady traffic this may not be an issue at all in practice, as containers will already be warm and re-used.
warm
– Enable active warming (Default: false)warm_count
– Number of concurrent containers to warm (Default: 15
)warm_rate
– Rate at which to perform the warming (Default: "15m"
)Here’s a example specifying 50
idle containers, for all remote stages (staging, production, and custom):
{ "name": "app", "lambda": { "warm": true, "warm_count": 50, "warm_rate": "30m" } }
Run up stack plan
and up stack apply
to make the changes to your stack! You may apply stage level changes as well, or enable warming for a specific stage only if desired, as shown here:
{ "name": "app", "stages": { "production": { "lambda": { "warm": true, "warm_count": 50 } } } }
Another way to mitigate cold starts is to use an uptime monitoring tool, such as Apex Ping which also monitors global performance, so it’s a win-win! Use the “up” coupon for 15% off your first year.
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 buildingbuild
– Run before building. Overrides inferred build command(s)postbuild
– Run after buildingpredeploy
– Run before deployingpostdeploy
– Run after deployingclean
– 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 section.
Multiple commands are 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.
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 deployment – see Ignoring Files. For example you may want an .upignore
containing:
* !public/**
Files are currently served from AWS Lambda as well, so there is a 6MB restriction on the file size.
If your project is not strictly static, for example a Node.js web app, you may omit type
and add static file serving simply by defining static
as shown below. With this setup Up will serve the file if it exists, before passing control to your application.
{ "name": "app", "static": { "dir": "public" } }
By default there is no prefix, so GET /index.css
will resolve to ./public/index.css
, however, you may specify a prefix such as “/static/” for GET /static/index.css
to ensure static files never conflict with your app’s routes:
{ "name": "app", "static": { "dir": "public", "prefix": "/static/" } }
Static file serving for dynamic apps does not automatically resolve index.html
files. The presence of a file is checked before passing control to your application.
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 “staging” or “production”Up Pro offers encrypted environment variables via the up env sub-command which supports per-stage environment variable mapping.
Environment variables defined via up env
take precedence over those defined in up.json.
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
By default Up will serve a minimalistic error page for requests accepting text/html
. The following settings are available:
disable
— remove the error page feature and default pagesdir
— the directory where the error pages are locatedvariables
— vars available to the pagesThe default template’s color
and optionally provide a support_email
to allow customers to contact your support team, for example:
{ "name": "site", "type": "static", "error_pages": { "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 5xx5xx.html
– Matches any 5xx error4xx.html
– Matches any 4xx errorCODE.html
– Matches a specific code such as 404.htmlVariables 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>
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 stringcomment
– An html commentstyle
– A style href
script
– A script src
inline style
– An inline styleinline script
– An inline scriptgoogle analytics
– Google Analytics snippet with API keysegment
– Segment snippet with API keyAll 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" }
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 } } }
More specific target paths take precedence over those which are less specific, for example /blog
will win over and /*
.
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, if the defaults are appropriate simply enable it as shown below.
{ "cors": { "enable": true } }
Suppose you have https://api.myapp.com
, you may want to customize cors
to permit access only from https://myapp.com
so that other sites cannot call your API directly.
{ "cors": { "allowed_origins": ["https://myapp.com"], "allowed_methods": ["HEAD", "GET", "POST", "PUT", "PATCH", "DELETE"], "allowed_headers": ["*"], "allow_credentials": true } }
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: ["*"]
)allowed_methods
– A list of methods the client is allowed to use with cross-domain requests. (Default: ["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: ["Origin", "Accept", "Content-Type", "X-Requested-With"]
)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. (Default: true
)debug
- A boolean which will output debug logs (Default: false
)Here’s an example performing a GraphQL query with fetch()
, note that Accept
is set to accept only JSON:
const body = JSON.stringify({ query: `query { pet(id: 2) { name } }` }) const res = await fetch('https://myapp.com', { headers: { 'Content-Type': 'application/json', Accept: 'application/json' }, method: 'POST', body })
You do not need to run up stack plan
for CORS settings, simply redeploy the stage.
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
)
package.json
is detected npm start
is usedapp.js
is detected node app.js
is usedapp.py
is detected python app.py
is usedtimeout
– 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
){ "proxy": { "command": "node app.js", "timeout": 10, "listen_timeout": 5 } }
Lambda’s function timeout is implied from the .proxy.timeout
setting.
Another benefit of using Up as a reverse proxy is performing crash recovery. Up will attempt to restart your application if the process crashes to continue serving subsequent requests.
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:
Up supports the concept of “stages” for configuration, such as mapping of custom domains, or tuning the size of Lambda function to use.
By default the following stages are defined:
development
— local development environmentstaging
— remote environment for staging new features or releasesproduction
— remote environment for productionTo create a new stage, first add it to your configuration, in this case we’ll call it “beta”:
{ "name": "app", "lambda": { "memory": 128 }, "stages": { "beta": { } } }
Now you’ll need to plan your stack changes, which will set up a new API Gateway and permissions:
$ up stack plan Add api deployment id: ApiDeploymentBeta Add lambda permission id: ApiLambdaPermissionBeta
Apply those changes:
$ up stack apply
Now you can deploy to your new stage by passing the name beta
and open the end-point in the browser:
$ up beta $ up url -o beta
To delete a stage, simply remove it from the up.json
configuration and run up stack plan
again, and up stack apply
after reviewing the changes.
You may of course assign a custom domain to these stages as well, let’s take a look at that next!
By defining a stage and its domain
, Up knows it will need to create a free SSL certificate—gh-polls.com
in the following example—setup the DNS records, and map the domain to API Gateway. SSL certificates are managed via AWS ACM which automatically renew for you, there’s no additional work or cost associated with them.
{ "stages": { "production": { "domain": "gh-polls.com" } } }
Here’s another example mapping each stage to a domain, note that the domains do not need to be related, you could use stage-gh-polls.com
for example.
{ "stages": { "production": { "domain": "gh-polls.com" }, "staging": { "domain": "stage.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. 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
.
CloudFront can take up to ~40 minutes to distribute this configuration the first time, so grab a coffee while these changes are applied. Also note that ACM certificates are always created in the Virginia (us-east-1) region due to how API Gateway interoperates with CloudFront.
By default when you specify a stage domain
— such as “api.example.com” — a DNS zone is created in Route53 for the top level domain “example.com”, and an ALIAS record “api.example.com” is added to this zone.
If you’re using external DNS and wish to omit the zone entirely you can disable it with the zone
property:
{ "stages": { "production": { "domain": "gh-polls.com", "zone": false } } }
You may also explicitly specify the zone by providing a string. In the following example an “api.gh-polls.com” zone will be created, instead of putting the record in “gh-polls.com”.
{ "stages": { "production": { "domain": "api.gh-polls.com", "zone": "api.gh-polls.com" } } }
Up allows some configuration properties to be overridden at the stage level. The following example illustrates how you can tune lambda memory and hooks per-stage.
{ "name": "app", "hooks": { "build": "parcel index.html --no-minify -o build", "clean": "rm -fr build" }, "stages": { "production": { "hooks": { "build": "parcel index.html -o build" }, "lambda": { "memory": 1024 } } } }
Currently the following properties may be specified at the stage level:
hooks
lambda
proxy.command
For example you may want to override proxy.command
for development, which is the env up start
uses. In the following example gin is used for hot reloading of Go programs:
{ "name": "app", "stages": { "development": { "proxy": { "command": "gin --port $PORT" } } } }
By default Up treats stdout as info
level logs, and stderr as error
level. If your logger uses stderr, such as Node’s debug()
module and you’d like to change this behaviour you may override these levels:
{ "name": "app", "environment": { "DEBUG": "myapp" }, "logs": { "stdout": "info", "stderr": "info" } }
Up supports gitignore style pattern matching for omitting files from deployment via the .upignore
file.
An example .upignore
to omit markdown and .go
source files might look like this:
*.md *.go
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/**
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.
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/**
Up Pro 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" } ] }
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.
The email action notifies your team via email.
{ "name": "email.backend", "type": "email", "emails": ["tj@apex.sh"] }
The sms action notifies your team via sms text message.
{ "name": "text.backend", "type": "sms", "numbers": ["+12508183100"] }
The slack action notifies your team via Slack message.
{ "name": "slack.backend", "type": "slack", "url": "https://hooks.slack.com/services/T0YS6H6S5/..." }
Optionally gifs
can be enabled and a channel
can be specified instead of using the default for the url
.
{ "name": "slack.backend", "type": "slack", "url": "https://hooks.slack.com/services/T0YS6H6S5/...", "channel": "alerts", "gifs": true }
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" }
metric
– Metric to alert against
http.count
– Request counthttp.latency
– Request latency in millisecondshttp.4xx
– HTTP 4xx client errorshttp.5xx
– HTTP 5xx server errorsstatistic
– Statistic name (“sum”, “min”, “max”, “avg”, “count”)threshold
– Threshold which is compared to operator
action
– Name of the action to performperiod
– 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: notBreaching
)disable
– Disable or mute the alertdescription
– Description of the alertUp 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:
When a package.json
file is detected, Node.js is the assumed runtime. By default the latest version supported by Lambda is used (nodejs8.10), see Lambda Settings for details.
The build
hook becomes:
$ npm run build
The server run by the proxy becomes:
$ npm start
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
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
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 crystallang/crystal crystal build -o server main.cr --release --static
The clean
hook becomes:
$ rm server
When a build.gradle
file is detected, Gradle is assumed, otherwise if pom.xml
is found then Maven is used.
When an index.html
file is detected the project is assumed to be static.
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
, up help <command>
, for example up help team members add
.
Usage: up [<flags>] <command> [<args> ...] Flags: -h, --help Output usage information. -C, --chdir="." Change working directory. -v, --verbose Enable verbose log output. --format="text" Output formatter. --version Show application version. Commands: help Show help for a command. build Build zip file. config Show configuration after defaults and validation. deploy Deploy the project. deploys Show deployment history. docs Open documentation website in the browser. domains ls List purchased domains. domains check Check availability of a domain. domains buy Purchase a domain. env add Add variables. env get Get a variable value. env ls List variables. env rm Remove variables. env export Export variables for the shell. logs Show log output. metrics Show project metrics. prune Prune old S3 deployments of a stage. rollback Rollback to a previous deployment. 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. team status Status of your account. team switch Switch active team. team login Sign in to your account. team logout Sign out of your account. team members add Add invites a team member. team members rm Remove a member or invite. team members ls List team members and invites. team subscribe Subscribe to the Pro plan. team unsubscribe Unsubscribe from the Pro plan. team card change Change the default card. team ci Credentials for CI. team add Add a new team. upgrade Install the latest or specified version of Up. url Show, open, or copy a stage endpoint. version Show version. Examples: Deploy the project to the staging environment. $ up Deploy the project to the production stage. $ up deploy production Show the staging endpoint url. $ up url Tail project logs. $ up logs -f Show error or fatal level logs. $ up logs 'error or fatal' Run build command manually. $ up run build Show help and examples for a command. $ up help team Show help and examples for a sub-command. $ up help team members
Deploy the project, by default to the “staging” stage. Note that running up
and up deploy
are identical, as it is the default command.
Usage: up deploy [<stage>] Flags: -h, --help Output usage information. -C, --chdir="." Change working directory. -v, --verbose Enable verbose log output. --format="text" Output formatter. --version Show application version. Args: [<stage>] Target stage name.
Deploy the project to the staging stage.
$ up
Deploy the project to the staging stage, this is the same as running up
without arguments.
$ up deploy
Deploy the project to the production stage.
$ up deploy production
Note that since deploy
is the default command the following is also valid:
$ up production
Up Pro supports instant rollbacks to a previous deployment. A subsequent up rollback
will revert the rollback, as it simply toggles the deployment versions. Note that rollbacks do not affect environment variables, as they are loaded at runtime.
Usage: up rollback [<flags>] [<version>] Flags: -h, --help Output usage information. -C, --chdir="." Change working directory. -v, --verbose Enable verbose log output. --format="text" Output formatter. --version Show application version. -s, --stage="staging" Target stage name. Args: [<version>] Target version for rollback.
Rollback to the previous staging version.
$ up rollback
Rollback to a specific git commit
$ up rollback 15c46ba
Rollback to a specific git tag
$ up rollback v1.7.2
Rollback to the previous production version.
$ up rollback -s production
Rollback to a specific git commit
$ up rollback -s production 15c46ba
Rollback to a specific git tag
$ up rollback -s production v1.7.2
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 } } ...
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. -C, --chdir="." Change working directory. -v, --verbose Enable verbose log output. --format="text" Output formatter. --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.
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: production version: 5 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: production status: 304 version: 5 1:36:34pm INFO request id: 8ff4bd57-c33a-11e7-bf4b-4f0d97c427c5 ip: 70.66.179.182 method: GET path: /static/1.png stage: production version: 5
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"
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'
Show, open, or copy a stage endpoint.
Usage: up url [<flags>] Flags: -h, --help Output usage information. -C, --chdir="." Change working directory. -v, --verbose Enable verbose log output. --format="text" Output formatter. --version Show application version. -s, --stage="staging" Target stage name. -o, --open Open endpoint in the browser. -c, --copy Copy endpoint to the clipboard.
Show the staging endpoint.
$ up url
Open the staging endpoint in the browser.
$ up url --open
Copy the staging endpoint to the clipboard.
$ up url --copy
Show the production endpoint.
$ up url -s production
Open the production endpoint in the browser.
$ up url -o -s production
Copy the production endpoint to the clipboard.
$ up url -c -s production
Show project metrics and estimated cost breakdown for requests, invocation count and the time spent for Lambda invocations.
Usage: up metrics [<flags>] Flags: -h, --help Output usage information. -C, --chdir="." Change working directory. -v, --verbose Enable verbose log output. --format="text" Output formatter. --version Show application version. -s, --stage="staging" Target stage name. -S, --since="1M" Show metrics since duration (30s, 5m, 2h, 1h30m, 3d, 1M).
For example:
$ up metrics -s 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 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.
See Stage Overrides for an example of overriding the proxy command per-stage, especially useful in development.
Up Pro supports environment variables, and these will be loaded with up start
, and variables mapped to the “development” stage will take precedence. For example:
$ up env set NAME Tobi $ up start # NAME is available as "Tobi" $ up env set NAME Loki -s development $ up start # NAME is available as "Loki" $ NAME=Jane up start # NAME is available as "Jane"
The UP_STAGE
and NODE_ENV
environment variables will be set to “development” automatically.
Usage: up start [<flags>] Flags: -h, --help Output usage information. -C, --chdir="." Change working directory. -v, --verbose Enable verbose log output. --format="text" Output formatter. --version Show application version. -c, --command=COMMAND Proxy command override -o, --open Open endpoint in the browser. --address=":3000" Address for server.
Start development server on port 3000.
$ up start
Start development server on port 5000.
$ up start --address :5000
Override proxy command. Note that the server created must listen on PORT
, which is why --port $PORT
is required for the gin example.
$ up start -c 'go run main.go' $ up start -c 'gin --port $PORT' $ up start -c 'node --some-flag app.js' $ up start -c 'parcel'
Manage domain names, and purchase them from AWS Route53 as the registrar.
Usage: up domains <command> [<args> ...] Flags: -h, --help Output usage information. -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.
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 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. -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.
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 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. -C, --chdir="." Change working directory. -v, --verbose Enable verbose log output. --format="text" Output formatter. --version Show application version. -s, --stage="staging" Target stage name. --size Show zip contents size information.
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
Manage team members, plans, and billing.
Usage: up team <command> [<args> ...] Flags: -h, --help Output usage information. -C, --chdir="." Change working directory. -v, --verbose Enable verbose log output. --format="text" Output formatter. --version Show application version. Subcommands: team status Status of your account. team switch Switch active team. team login Sign in to your account. team logout Sign out of your account. team members add Add invites a team member. team members rm Remove a member or invite. team members ls List team members and invites. team subscribe Subscribe to the Pro plan. team unsubscribe Unsubscribe from the Pro plan. team card change Change the default card. team ci Credentials for CI. team add Add a new team.
Show active team and subscription status.
$ up team
Switch teams interactively.
$ up team switch
Sign in or create account with interactive prompt.
$ up team login
Sign in to a team.
$ up team login --email tj@example.com --team apex-software
Add a new team and automatically switch to the team.
$ up team add "Apex Software"
Subscribe to the Pro plan.
$ up team subscribe
Invite a team member to your active team.
$ up team members add asya@example.com
Install the latest or specified version of Up. The OSS and Pro versions have independent semver, as bugfixes and features for one may not be relevant to the other.
If you’re an Up Pro subscriber, up upgrade
will always install Up Pro, even when --target
is specified, there is no need to specify that you want the Pro version.
Usage: up upgrade [<flags>] Flags: -h, --help Output usage information. -C, --chdir="." Change working directory. -v, --verbose Enable verbose log output. --format="text" Output formatter. --version Show application version. -t, --target=TARGET Target version for upgrade.
Upgrade to the latest version available.
$ up upgrade
Upgrade to the specified version.
$ up upgrade -t 0.4.4
Prune old S3 deployments of a stage.
Usage: up prune [<flags>] Flags: -h, --help Output usage information. -C, --chdir="." Change working directory. -v, --verbose Enable verbose log output. --format="text" Output formatter. --version Show application version. -s, --stage="staging" Target stage name. -r, --retain=30 Number of versions to retain.
Prune and retain the most recent 30 staging versions.
$ up prune
Prune and retain the most recent 30 production versions.
$ up prune -s production
Prune and retain the most recent 15 production versions.
$ up prune -s production -r 15
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. -C, --chdir="." Change working directory. -v, --verbose Enable verbose log output. --format="text" Output formatter. --version Show application version. Subcommands: env add Add variables. env get Get a variable value. env ls List variables. env rm Remove variables.
Here are some examples:
$ up env add DB_NAME users DB_USER sloth $ up env add DB_NAME=users 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
Get environment variable values with:
$ up env get DB_NAME $ up env get DB_NAME -s production
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
Another trick is evaluating the env variables in your shell so that other programs have access to them, for example:
$ eval "$(up env export)" $ echo "name=$DB_NAME user=$DB_USER" name=users user=sloth
Environment variables are “baked” into the Lambda configuration upon deploy, thus must re-deploy the application stage(s) to see any environment variable changes. This reduces cold starts by roughly 600-800ms compared to loading env vars at runtime.
Variables defined via up env
are also available to the build hooks and up start
. The UP_STAGE
and NODE_ENV
are defined to the target stage name automatically, unless specified manually. See the Env Static example to see how it works with build hooks.
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 team login email: tj@apex.sh ⠋ verify: Check your email for a confirmation link
Click the link in your email and you’re signed in! If you’re using Up Pro with one or more organizations, you should create a team to manage team members and subscriptions independently. If you plan on using Up Pro for personal use you may skip this step.
$ up team add "My Company"
Next you’ll need to subscribe! You’ll be asked for an optional coupon, credit card information – which never touches our servers, only Stripe via HTTPS – and finally a subscription confirmation.
$ up team subscribe
Now you and your team members may upgrade to 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 team team: apex subscription: Up Pro amount: $10.00/mo USD created: December 22, 2017
To switch to another team run the following and select the active team.
$ up team 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.
To invite members use the following command:
$ up team members add tobi@apex.sh $ up team members add loki@apex.sh $ up team members add jane@apex.sh
At any time you can view invites and members:
$ up team members team: apex Members • tj@apex.sh • tobi@apex.sh • loki@apex.sh Invites • jane@apex.sh
Your team members will receive an email with installation instructions, where they run the following to sign in – with your team id of course.
$ up team login --email tobi@apex.sh --team apex
This section guides you through taking a small application from development, to production, complete with purchasing and mapping a custom domain.
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
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.
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 staging and production. Note that up
is an alias of up deploy staging
.
$ up $ up deploy production
Open both in the browser:
$ up url -o $ up url -s production -o
You should see “Hello World from production” and “Hello World from staging”.
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 stage.up-example.com
for staging.
{ "name": "up-example", "profile": "up-tobi", "stages": { "staging": { "domain": "stage.up-example.com" }, "production": { "domain": "up-example.com" } } }
Note that you could map staging to a domain like staging-myapp.com
, 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. AWS sends an email to the 3 contact addresses listed in WHOIS when you registered the domain, and to the following 5 common system addresses for your domain:
See Validate Domain Ownership for more information.
After clicking “I Approve” in one of the emails, 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:
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://stage.up-example.com/ will point to the latest deployment via up
.
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.
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 top-level domain sloths.com
is managed by Cloudflare and you’d like point api.sloths.com
to your app, you should first add it to your up.json
:
{ "name": "sloths" "stages": { "production": { "domain": "api.sloths.com" } } }
Next you will need to up stack plan
and up stack apply
, this will set up a CloudFront end-point for the application. To view the endpoint information, run up stack
:
$ up stack Production domain: api.sloths.com endpoint: d72wsqljqg5cy.cloudfront.net
In your DNS provider – Cloudflare in this example – you should create a CNAME
record pointing to the production endpoint
. Make sure that the domain
you use matches the domain in Cloudflare.
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": { "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
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
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.
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 keyAWS_SECRET_ACCESS_KEY
– AWS secret keyIf using running Up Pro you’ll need your Up credentials in order to access Up Pro via the up upgrade
command. To obtain this run up team ci
or up team ci --copy
to copy it directly to your clipboard, then paste this as the env var’s value.
UP_CONFIG
– Up configuration as base64-encoded JSONIf you run into “403 Forbidden” errors this is due to GitHub’s low rate limit for unauthenticated users, consider creating a Personal Access Token and adding the following variable to your CI:
GITHUB_TOKEN
— Github personal access tokenYou may install Up in the current working directory, and deploy to production with the following commands, omitting the up upgrade
if you are not an Up Pro subscriber.
$ curl -sf https://up.apex.sh/install | BINDIR=. sh $ ./up upgrade $ ./up production
Or if you prefer installing globally within PATH
:
$ sudo chown -R $(whoami) /usr/local/bin $ curl -sf https://up.apex.sh/install | sh $ up upgrade $ up production
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.
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
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.
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.
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"
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"
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"
The >
, >=
, <
, and <=
relational operators are useful for comparing numeric values, for example response status codes:
status >= 200 status < 300
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
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
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")
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
)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"
The up start
command uses your proxy.command
by default, which may be inferred based on your application type, such as node app.js
for Node.js or ./server
for Golang.
You may alter this command for up start
with the development environment. For example with Golang you may want go run main.go
, or hot reloading with gin as shown here:
{ "name": "app", "stages": { "development": { "proxy": { "command": "gin --port $PORT" } } } }
Note that the server must always listen on PORT
which is provided by up start
.
Traditional AWS Lambda functions provided a context object which contains runtime information such as API Gateway user identity. This information is exposed as JSON in the X-Context
header field in Up as shown here:
const http = require('http') const { PORT } = process.env const app = http.createServer((req, res) => { const ctx = JSON.parse(req.headers['x-context']) res.end(JSON.stringify(ctx, null, 2)) }) console.log('Starting app on %s', PORT) app.listen(PORT)
Output will be similar to the following. Visit the AWS Documentation for details.
{ "apiId": "g4yn392afg", "resourceId": "ez0z8areob", "requestId": "d8314ef1-5543-11e8-a925-21fa0dd01c37", "accountId": "337344593553", "stage": "staging", "identity": { "apiKey": "", "accountId": "", "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36", "sourceIp": "64.110.31.100", "accessKey": "", "caller": "", "user": "", "userARN": "", "cognitoIdentityId": "", "cognitoIdentityPoolId": "", "cognitoAuthenticationType": "", "cognitoAuthenticationProvider": "" }, "authorizer": null }
Up automatically detects and utilizes Git commit information when you deploy, such as recording the Git commit SHA or tag, and author. This information can be used for:
When deploying from a Git repo the commit is also available in the logs for display and filtering, for example the tag v1.8.2
in the following log output:
Mar 15th 11:32:54am INFO production v1.8.2 request: id=467991e8-287f-11e8-bd4e-0ba9f514a0f0 ip=207.194.38.50 method=GET path=/favicon.ico Mar 15th 11:32:54am INFO production v1.8.2 response: duration=0s id=467991e8-287f-11e8-bd4e-0ba9f514a0f0 ip=207.194.38.50 method=GET path=/favicon.ico size=66 B status=200
Filtering can be performed with the commit
field:
$ up logs 'commit = "v1.8.2"'
Up Pro allows you to view deployment history via up deploys
which will display the Git SHA, tag, or Lambda version if you are not using Git for the project.
$ up deploys Stage Version Author Date production v1.8.0 TJ Holowaychuk 22 minutes ago production v1.7.3 TJ Holowaychuk 23 minutes ago staging f4f3143 TJ Holowaychuk 24 minutes ago staging ca781f7 TJ Holowaychuk 24 minutes ago production v1.7.2 TJ Holowaychuk 56 minutes ago staging v1.7.2 TJ Holowaychuk 2 hours ago production 7e62daf TJ Holowaychuk 2 hours ago production v1.7.1 TJ Holowaychuk 2 hours ago staging v1.7.1 TJ Holowaychuk 2 hours ago staging 15c46ba TJ Holowaychuk 2 hours ago staging 15c46ba TJ Holowaychuk 2 hours ago staging 74f9a92 TJ Holowaychuk 2 hours ago
As mentioned in the rollback command section, with Up Pro you may use the Git tag or SHA for reverting to an older release:
$ up rollback -s production v1.8.0
AWS email delivery can be slow sometimes. Please give it 30-60s. Otherwise, be sure to check your spam folder.
Lambda memory
scales CPU alongside RAM, so if your application is slow to initialize or serve responses, you may want to try 1024
or above. See Lambda Pricing for options.
Ensure that all of your dependencies are deployed. You may use up -v
to view what is added or filtered from the deployment or up build --size
to output the contents of the zip.
By default, Up ignores files which are found in .upignore
. Use the verbose flag such as up -v
to see if files have been filtered or up build --size
to see a list of files within the zip sorted by size. See Ignoring Files for more information.
The first deploy also creates resources associated with your project and can take roughly 1-2 minutes. AWS provides limited granularity into the creation progress of these resources, so the progress bar may appear “stuck”.
Run up team login
if you aren’t signed in, then run up team login --team my-team-id
to sign into any teams you’re an owner or member of.
If you receive a Unable to associate certificate
error it is because you have not verified the SSL certificate. Certs for CloudFront when creating a custom domain MUST be in us-east-1, so if you need to manually resend verification emails visit ACM in US East 1.
If you run into “403 Forbidden” errors this is due to GitHub’s low rate limit for unauthenticated users, consider creating a Personal Access Token and adding GITHUB_TOKEN
to your CI.
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.
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 of 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.
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.
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.
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.
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.
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.
With a 512mb Lambda function Up introduces an average of around 500µs (microseconds) per request.
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.
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.
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.
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.
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 :).