> ## Documentation Index
> Fetch the complete documentation index at: https://developer.upsun.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Up(sun) and running with Lisp

export const PostMeta = ({data = {}}) => {
  const {author, date, image} = data;
  const authors = Array.isArray(author) ? author : author ? [author] : [];
  const resolveAuthor = slug => {
    const entry = AUTHOR_MAP[slug] || ({});
    const name = entry.name || slug;
    const github = entry.github || null;
    const linkedin = entry.linkedin || null;
    const url = github ? `https://github.com/${github}` : linkedin || null;
    const avatarUrl = github ? `https://github.com/${github}.png?size=64` : null;
    return {
      name,
      url,
      avatarUrl
    };
  };
  const formattedDate = date ? new Date(date).toLocaleDateString('en-US', {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  }) : null;
  if (!image && authors.length === 0 && !formattedDate) return null;
  const AUTHOR_MAP = {
    "aaron-collier": {
      "name": "Aaron Collier"
    },
    "aaron-dudenhofer": {
      "name": "Aaron Dudenhofer"
    },
    "aaron-porter": {
      "name": "Aaron Porter"
    },
    "adriaan-odendaal": {
      "name": "Adriaan Odendaal"
    },
    "ajmal": {
      "name": "Ajmal Siddiqui"
    },
    "akalipetis": {
      "name": "Antonis Kalipetis"
    },
    "alexander-varwijk": {
      "name": "Alexander Varwijk"
    },
    "alicia-bevilacqua": {
      "name": "Alicia Bevilacqua"
    },
    "amelie-deguerry": {
      "name": "Amelie Deguerry"
    },
    "anacidre": {
      "name": "Ana Cidre",
      "linkedin": "https://www.linkedin.com/in/ana-cidre"
    },
    "andoni": {
      "name": "Andoni Auzmendi"
    },
    "andrei-taranu": {
      "name": "Andrei (Alex) Taranu",
      "linkedin": "https://www.linkedin.com/in/andrei-alex-taranu/"
    },
    "andrew-baxter": {
      "name": "Andrew Baxter"
    },
    "andrew-melck": {
      "name": "Andrew Melck"
    },
    "antoine-crochet-damais": {
      "name": "Antoine Crochet Damais"
    },
    "augustin-delaporte": {
      "name": "Augustin Delaporte",
      "linkedin": "https://www.linkedin.com/in/augustindelaporte/"
    },
    "branislav-bujisic": {
      "name": "Branislav Bujisic"
    },
    "carl-smith": {
      "name": "Carl Smith"
    },
    "caroline-leroy": {
      "name": "Caroline Leroy"
    },
    "cati-mayer": {
      "name": "Cati Mayer"
    },
    "catplat": {
      "name": "C Trinkwon"
    },
    "ceelolulu": {
      "name": "Celeste van der Watt"
    },
    "chadwcarlson": {
      "name": "Chad Carlson",
      "github": "chadwcarlson",
      "linkedin": "https://www.linkedin.com/in/chadwcarlson"
    },
    "chris-ward": {
      "name": "Chris Ward"
    },
    "chris-yates": {
      "name": "Chris Yates"
    },
    "christian-sieber": {
      "name": "Christian Sieber"
    },
    "christopher-lockheardt": {
      "name": "Christopher Lockheardt"
    },
    "christopher-skene": {
      "name": "Christopher Skene"
    },
    "chuck-morgan": {
      "name": "Chuck Morgan"
    },
    "corey-dockendorf": {
      "name": "Corey Dockendorf"
    },
    "crell": {
      "name": "Crell"
    },
    "damz": {
      "name": "Damz"
    },
    "dan-morrison": {
      "name": "Dan Morrison"
    },
    "davidbonachera": {
      "name": "David Bonachera",
      "github": "davidbonachera",
      "linkedin": "https://www.linkedin.com/in/davidbonachera"
    },
    "dereliahmet1": {
      "name": "Ahmet Faruk Dereli"
    },
    "devicezero": {
      "name": "Jonas Kröger",
      "github": "devicezero",
      "linkedin": "https://www.linkedin.com/in/jonaskroeger/"
    },
    "doug-goldberg": {
      "name": "Doug Goldberg"
    },
    "duncan-naves": {
      "name": "Duncan Naves",
      "github": "duncannaves",
      "linkedin": "https://www.linkedin.com/in/duncan-naves-a94423aa"
    },
    "erika-bustamante": {
      "name": "Erika Bustamante"
    },
    "fabpot": {
      "name": "Fabien Potencier"
    },
    "flovntp": {
      "name": "Florent Huck",
      "github": "flovntp",
      "linkedin": "https://www.linkedin.com/in/florenthuck"
    },
    "fred-plais": {
      "name": "Fred Plais"
    },
    "gauthier-garnier": {
      "name": "Gauthier Garnier"
    },
    "gilzow": {
      "name": "Paul Gilzow"
    },
    "gmoigneu": {
      "name": "Guillaume Moigneu",
      "github": "gmoigneu",
      "linkedin": "https://www.linkedin.com/in/guillaumemoigneu/"
    },
    "gregqualls": {
      "name": "Greg Qualls"
    },
    "guguss": {
      "name": "Augustin Delaporte"
    },
    "haylee-millar": {
      "name": "Haylee Millar"
    },
    "ivana-kotur": {
      "name": "Ivana Kotur"
    },
    "jackrabbithanna": {
      "name": "Mark Hanna"
    },
    "jared-wright": {
      "name": "Jared Wright",
      "github": "jww-sh",
      "linkedin": "https://www.linkedin.com/in/jaredwaynewright"
    },
    "jessica-orozco": {
      "name": "Jessica Orozco"
    },
    "joey-stanford": {
      "name": "Joey Stanford"
    },
    "john-grubb": {
      "name": "John Grubb"
    },
    "jonas-kruger": {
      "name": "Jonas Kruger"
    },
    "kathryn-frazer": {
      "name": "Kathryn Frazer"
    },
    "kemiojo": {
      "name": "Kemi Elizabeth Ojogbede"
    },
    "kieronsambrook-smith": {
      "name": "Kieronsambrook Smith"
    },
    "laurent-arnoud": {
      "name": "Laurent Arnoud"
    },
    "letoya-boyne": {
      "name": "Letoya Boyne"
    },
    "lolautruche": {
      "name": "Jérôme Vieilledent"
    },
    "lyly-lepinay": {
      "name": "Lyly Lepinay"
    },
    "manauwar-alam": {
      "name": "Manauwar Alam"
    },
    "marc-antoine-porri": {
      "name": "Marc Antoine Porri"
    },
    "maria-antinkaapo": {
      "name": "Maria Antinkaapo"
    },
    "maria-de-anton": {
      "name": "Maria De Anton"
    },
    "mark-dorison": {
      "name": "Mark Dorison"
    },
    "markus-hausammann": {
      "name": "Markus Hausammann"
    },
    "mary-thomas": {
      "name": "Mary Thomas"
    },
    "mathias-bolt-lesniak": {
      "name": "Mathias Bolt Lesniak"
    },
    "mathieu-strauch": {
      "name": "Mathieu Strauch"
    },
    "matthias-van-woensel": {
      "name": "Matthias Van Woensel",
      "linkedin": "https://www.linkedin.com/in/matthias-van-woensel-267a069"
    },
    "michael-sharp": {
      "name": "Michael Sharp"
    },
    "mupsi": {
      "name": "Marine Gandy"
    },
    "natalie-harper": {
      "name": "Natalie Harper"
    },
    "ngommenginger": {
      "name": "Nicolas Gommenginger",
      "linkedin": "https://www.linkedin.com/in/nicolas-gommenginger"
    },
    "nicholas-bennison": {
      "name": "Nicholas Bennison"
    },
    "nicholas-vahalik": {
      "name": "Nicholas Vahalik"
    },
    "nick-hardiman": {
      "name": "Nick Hardiman"
    },
    "nickanderegg": {
      "name": "Nickanderegg"
    },
    "nicolas-grekas": {
      "name": "Nicolas Grekas",
      "github": "nicolas-grekas",
      "linkedin": "https://www.linkedin.com/in/nicolasgrekas/"
    },
    "niti-malwade": {
      "name": "Niti Malwade"
    },
    "opensocialteam": {
      "name": "Opensocialteam"
    },
    "ori-pekelman": {
      "name": "Ori Pekelman"
    },
    "otavio-santana": {
      "name": "Otavio Santana"
    },
    "palwandi": {
      "name": "Pawan Alwandi",
      "github": "pawpy",
      "linkedin": "https://www.linkedin.com/in/pawanalwandi"
    },
    "patrick-boest": {
      "name": "Patrick Boest"
    },
    "patrick-dawkins": {
      "name": "Patrick Dawkins",
      "github": "pjcdawkins",
      "linkedin": "https://www.linkedin.com/in/patrickdawkins"
    },
    "patrick-klima": {
      "name": "Patrick Klima"
    },
    "pjcdawkins": {
      "name": "Pjcdawkins"
    },
    "prineet-kaurbhurji": {
      "name": "Prineet Kaurbhurji"
    },
    "quentin-sinig": {
      "name": "Quentin Sinig"
    },
    "ralt": {
      "name": "Florian Margaine",
      "github": "ralt",
      "linkedin": "https://www.linkedin.com/in/florian-margaine-43971136"
    },
    "ramanathanramakrishnamurthy": {
      "name": "Ramanathanramakrishnamurthy"
    },
    "remi-lejeune": {
      "name": "Rémi Lejeune"
    },
    "ribel": {
      "name": "Taras Kruts"
    },
    "robert-douglass": {
      "name": "Robert Douglass"
    },
    "rudy-weber": {
      "name": "Rudy Weber"
    },
    "ryan-hicks": {
      "name": "Ryan Hicks"
    },
    "sabri-helal": {
      "name": "Sabri Helal"
    },
    "savannah-bergeron": {
      "name": "Savannah Bergeron"
    },
    "shannon-vettes": {
      "name": "Shannon Vettes"
    },
    "shawn-ogasawara": {
      "name": "Shawn Ogasawara",
      "linkedin": "https://www.linkedin.com/in/shawn-ogasawara-83a9a0/"
    },
    "shawna-spoor": {
      "name": "Shawna Spoor"
    },
    "shedrack-akintayo": {
      "name": "Shedrack Akintayo"
    },
    "simon-ruggier": {
      "name": "Simon Ruggier"
    },
    "sophie-van-der-kindere": {
      "name": "Sophie Van Der Kindere"
    },
    "stefanos-thampis": {
      "name": "Stefanos Thampis"
    },
    "stephen-weinberg": {
      "name": "Stephen Weinberg"
    },
    "sukhman-virk": {
      "name": "Sukhman Virk"
    },
    "sumaira-nazir": {
      "name": "Sumaira Nazir"
    },
    "sumer": {
      "name": "Sümer Cip"
    },
    "syed-raza": {
      "name": "Syed Raza"
    },
    "tamara-bacchia": {
      "name": "Tamara Bacchia"
    },
    "tara-arnold": {
      "name": "Tara Arnold"
    },
    "theosakamg": {
      "name": "Mickael Gaillard",
      "github": "theosakamg"
    },
    "thomasdiluccio": {
      "name": "Thomas di Luccio"
    },
    "tim-anderson": {
      "name": "Tim Anderson"
    },
    "tom-helmer-hansen": {
      "name": "Tom Helmer Hansen"
    },
    "tylermills": {
      "name": "Tyler Mills"
    },
    "upsun": {
      "name": "Upsun"
    },
    "veronika-tolkachova": {
      "name": "Veronika Tolkachova",
      "linkedin": "https://www.linkedin.com/in/veronika-tolkachova-169167a2"
    },
    "vince-parker": {
      "name": "Vince Parker"
    },
    "vinnie-russo": {
      "name": "Vincenzo Russo"
    },
    "vrobert78": {
      "name": "Vincent Robert",
      "github": "vrobert78",
      "linkedin": "https://www.linkedin.com/in/vincent-robert-498a883"
    },
    "yuriy-babenko": {
      "name": "Yuriy Babenko"
    },
    "yuriy-gerasimov": {
      "name": "Yuriy Gerasimov"
    }
  };
  return <div className="post-meta">
      {(authors.length > 0 || formattedDate) && <div className="post-meta-info">
          {authors.length > 0 && <div className="post-meta-authors">
              {authors.map(slug => {
    const {name, url, avatarUrl} = resolveAuthor(slug);
    const inner = <>
                    {avatarUrl && <img src={avatarUrl} alt={name} className="post-meta-avatar" />}
                    <span className="post-meta-author-name">{name}</span>
                  </>;
    return url ? <a key={slug} href={url} target="_blank" rel="noopener noreferrer" className="post-meta-author">
                    {inner}
                  </a> : <span key={slug} className="post-meta-author">{inner}</span>;
  })}
            </div>}
          {authors.length > 0 && formattedDate && <span className="post-meta-separator" aria-hidden="true">·</span>}
          {formattedDate && <span className="post-meta-date">{formattedDate}</span>}
        </div>}
      {image && <img src={image} alt="" className="post-meta-image" aria-hidden="true" />}
    </div>;
};

<PostMeta data={{ author: ["kemiojo"], date: "2025-03-17", image: "/images/posts/tutorials/deploying-with-lisp/blog-lisp-blog.webp" }} />

<Note>
  Lisp support was discontinued in March 2025. If you really want to power a Lisp project, feel free to contact us on our [Discord](https://discord.gg/upsun).
</Note>

Lisp, created by John McCarthy in 1958, is one of the oldest programming languages, renowned for its unique syntax where code and data share the same structure. It's commonly used in fields like artificial intelligence and functional programming, with its main strengths being its flexibility and suitability for recursive data structures.

In this step-by-step guide, you will learn how to quickly deploy a Lisp web application on both Platform.sh and Upsun. So, without further ado, let's jump right in:

## Write your Lisp program

For this guide we will be using a basic example of a Hunchentoot-based web app
(you can find the corresponding `.asd` and `.yaml` files further down):

```lisp theme={null}
(defpackage #:example
  (:use :hunchentoot :cl-who :cl)
  (:export main))

(in-package #:example)

(define-easy-handler (greet :uri "/hello") (name)
  (with-html-output-to-string (s) (htm (:body (:h1 "hello, " (str name))))))

(defun main ()
  (let ((acceptor (make-instance
                   'easy-acceptor
                   :port (parse-integer (uiop:getenv "PORT")))))
    (start acceptor)
    (sleep most-positive-fixnum)))
```

Notice how the app gets the `PORT` from the environment and sleeps at the end as `(start acceptor)` yields and the apps run in the foreground as required.

This code is surprisingly legible. For example, `use` means "import all the symbols so there's no need to prefix them with the package name" and `cl-who` allows the actual HTML to be created.

## Specify the language in your app

Next, make sure that you specify `lisp` as your app’s type in your config.yaml:

<Tabs>
  <Tab title="Platform.sh">
    ```yaml {filename=".platform.app.yaml"} theme={null}
    type: 'lisp:2.1'
    ```
  </Tab>

  <Tab title="Upsun">
    ```yaml {filename=".upsun/config.yaml"} theme={null}
    applications:
      # The app's name, which must be unique within the project.
      <APP_NAME>:
        type: 'lisp:2.1'
    ```
  </Tab>
</Tabs>

## Handle dependencies

The recommended way to handle Lisp dependencies with Upsun and Platform.sh is through using ASDF.

ASDF is a description of dependencies between files of source code in such a way that they can be compiled and loaded in the right order.

Create an `.asd` file. When an `.asd` file is found in your repository, the system will automatically download the dependencies using QuickLisp.

Below is an `example.asd` file:

```asd {filename="example.asd"} theme={null}
(defsystem example
 :name "example"
 :description "Example of a simple web application on Platform.sh"
 :author "Lisp Coder <user@example.com>"
 :components ((:file "example"))
 :build-operation "asdf:program-op"
 :build-pathname "example"
 :entry-point "example:main"
 :depends-on (:hunchentoot :cl-who))
```

## Assumptions

Before we continue, the following assumptions have been made about your application to provide a more streamlined experience. These assumptions are the following:

* Your `.asd` file is named like your system name. For example, our application above is named `example.asd`  and has `(defsystem example ...)` in the `.asd` file.

Therefore `(asdf:make :example)` will run on our system to build a binary.

If you don't want these assumptions, you can disable this behavior by specifying in your `config.yaml`:

<Tabs>
  <Tab title="Platform.sh">
    ```yaml {filename=".platform.app.yaml"} theme={null}
    build:
      flavor: none
    ```
  </Tab>

  <Tab title="Upsun">
    ```yaml {filename=".upsun/config.yaml"} theme={null}
    applications:
      myapp:
        type: 'lisp:2.1'
        build:
          flavor: none
    ```
  </Tab>
</Tabs>

Okay, let's proceed...

## Specify QuickLisp distrubutions

If you wish to change the distributions that QuickLisp is using, you can specify those as follows, specifying a distribution name, its URL and, an optional version:

<Tabs>
  <Tab title="Platform.sh">
    ```yaml {filename=".platform.app.yaml"} theme={null}
    runtime:
      quicklisp:
        DISTRIBUTION_NAME:
          url: "..."
          version: "..."
    ```
  </Tab>

  <Tab title="Upsun">
    ```yaml {filename=".upsun/config.yaml"} theme={null}
    applications:
      # The app's name, which must be unique within the project.
      <APP_NAME>:
        type: 'lisp:2.1'
        runtime:
          quicklisp:
            DISTRIBUTION_NAME:
              url: "..."
              version: "..."
    ```
  </Tab>
</Tabs>

For example:

<Tabs>
  <Tab title="Platform.sh">
    ```yaml {filename=".platform.app.yaml"} theme={null}
    runtime:
      quicklisp:
        quicklisp:
          url: 'http://beta.quicklisp.org/dist/quicklisp.txt'
          version: '2025-03-17'
    ```
  </Tab>

  <Tab title="Upsun">
    ```yaml {filename=".upsun/config.yaml"} theme={null}
    applications:
      # The app's name, which must be unique within the project.
      myapp:
        type: 'lisp:2.1'
        runtime:
          quicklisp:
            quicklisp:
              url: 'http://beta.quicklisp.org/dist/quicklisp.txt'
              version: '2025-03-17'
    ```
  </Tab>
</Tabs>

## Get built-in variables

Upsun and Platform.sh expose relationships and other configurations as [environment variables](https://docs.upsun.com/development/variables.html). To get the `PORT` environment variable (the port on which your web application is supposed to listen):

```lisp theme={null}
(parse-integer (uiop:getenv "PORT"))
```

## Build and run your application

Assuming `example.lisp` and `example.asd` are present in your repository, the app is automatically built on push.

You can then start it from the `web.commands.start` directive.

Note that the start command *must* run in the foreground. Should the program terminate for any reason it's automatically restarted. In the example below the app sleeps for a *very* long time. You could also choose to join the thread of your web server, or use other methods to make sure the program doesn't terminate.

Create a `.yaml` containing something like what is shown below:

<Tabs>
  <Tab title="Platform.sh">
    ```yaml {filename=".platform.app.yaml"} theme={null}
    name: myapp
    type: lisp:2.1
    web:
      commands:
        start: ./example
      locations:
        /:
          allow: false
          passthru: true
    disk: 512
    ```
  </Tab>

  <Tab title="Upsun">
    ```yaml {filename=".upsun/config.yaml"} theme={null}
    applications:
      myapp:
        type: 'lisp:2.1'
        web:
          commands:
            start: ./example
          locations:
            /:
              allow: false
              passthru: true
    ```
  </Tab>
</Tabs>

Note that a proxy server is still in front of your app. If desired, certain paths may be served directly by the router without hitting your app (for static files, primarily) or you may route all requests to the Lisp application unconditionally, as in the example above.

## Access services

The services configuration is available in the environment variable `PLATFORM_RELATIONSHIPS`.

To parse them, add the dependencies to your `.asd` file:

```lisp theme={null}
:depends-on (:jsown :babel :s-base64)
```

The following is an example of accessing a PostgreSQL instance:

```lisp theme={null}
(defun relationships ()
  (jsown:parse
   (babel:octets-to-string
    (with-input-from-string (in (uiop:getenv "PLATFORM_RELATIONSHIPS"))
      (s-base64:decode-base64-bytes in)))))
```

Given a relationship defined in your `.yaml` config file:

<Tabs>
  <Tab title="Platform.sh">
    ```yaml {filename=".platform.app.yaml"} theme={null}
    relationships:
      postgresql:
    ```

    <Note>
      Depending on your needs, instead of the default endpoint configuration above, you can use an [explicit endpoint configuration](https://docs.platform.sh/create-apps/app-reference/single-runtime-image.html#relationships).
    </Note>
  </Tab>

  <Tab title="Upsun">
    ```yaml {filename=".upsun/config.yaml"} theme={null}
    applications:
      myapp:
        type: 'lisp:2.1'
        relationships:
          postgresql:
    ```

    <Note>
      Depending on your needs, instead of the default endpoint configuration above, you can use an [explicit endpoint configuration](https://docs.platform.sh/create-apps/app-reference/single-runtime-image.html#relationships).\`\`\`
    </Note>
  </Tab>
</Tabs>

The following would access that relationship, and provide your Lisp program the credentials to connect to a PostgreSQL instance.

Add this to your `.asd` file:

```lisp theme={null}
:depends-on (:postmodern)
```

Now, in your program you can access the PostgreSQL instance as follows:

```lisp theme={null}
(defvar *pg-spec* nil)

(defun setup-postgresql ()
  (let* ((pg-relationship (first (jsown:val (relationships) "postgresql")))
         (database (jsown:val pg-relationship "path"))
         (username (jsown:val pg-relationship "username"))
         (password (jsown:val pg-relationship "password"))
         (host (jsown:val pg-relationship "host")))
    (setf *pg-spec*
      (list database username password host)))
  (postmodern:with-connection *pg-spec*
    (unless (member "example_table" (postmodern:list-tables t) :test #'string=)
      (postmodern:execute "create table example_table (
    a_field TEXT NOT NULL UNIQUE,
    another_field TEXT NOT NULL UNIQUE
")))))
```

## Conclusion

In conclusion, deploying a Lisp-based web application on Platform.sh and Upsun is a straightforward process once you understand the key configurations and dependencies involved. From setting up your Lisp program to managing environment variables and service relationships, you can easily get your application up(sun) and running.

<Note>
  Once again, while Lisp’s support is discontinued as of March 2025, the language remains a valuable tool for certain projects. If you're looking to power a Lisp application with [`composable image`](https://docs.upsun.com/create-apps/app-reference/composable-image.html) in the future, don't hesitate to reach out via our [Discord](https://discord.gg/upsun) for assistance.
</Note>
