If you like my work, please consider supporting its development.

This guide walks the user through configuring Nebula Forms to handle a contact form by sending an email to the site administrator, as an example for how to configure Nebula.

This guide assumes the following:

  • The contact form is found at https://example.com/contact/
  • Nebula Forms is hosted at https://forms.example.com/
  • The server is running on a Linux system
  • The reader has some knowledge of UNIX-y systems and, specifically, how to become the root user.

The Form

This form does not need to be copied to any website, as this guide is meant to be an introduction to configuring Nebula; you are encouraged to modify any options you'd like, assuming you understand what they mean. The form code is only provided to give context to the options used while setting up the server.

<form name="contact" method="POST" action="https://forms.example.com/contact">
    <label>Name: <input name="name" type="text" required></label>
    <label>Email: <input name="email" type="email" required></label>
    <input type="text" class="hide" name="confirm_email">
    <label>Summary: <input name="summary" type="text" required></label>
    <label for="explanation">Explain:</label>
    <textarea name="explanation" id="reason" required></textarea>
    <button type="submit">Submit</button>

This form, if rendered as-is, would have every input and label in a line next to each other. If you are coding your own form, don't copy the above one. It's just for context.

Creating the Configuration File

Create the folder /etc/nebula-forms (/usr/local/etc/nebula-forms on FreeBSD). This folder is the default location for configuration files for Nebula.

The below commands will need to be run as the root user.

mkdir /etc/nebula-forms
mkdir /usr/local/etc/nebula-forms

Open the file /etc/nebula-forms/config.toml for editing. Most users will find it easiest to use the nano editor. This guide will give instructions for nano and assumes people using Vim/Emacs know what they're doing.

nano /etc/nebula-forms/config.toml
pkg install nano
nano /usr/local/etc/nebula-forms/config.toml

Configuring the Server

Enter the following into the newly-opened file.

log_file = "/var/log/nebula-forms/access.log"
error_file = "/var/log/nebula-forms/error.log"
include_dir = "conf.d"

Save and quit by pressing Ctrl+x (exit), then ‘y’ (yes, save changes) and the enter key (confirm file name).

The first two options set the locations of log files to their default values.

The third option tells Nebula that it can find more configuration files in the directory /etc/nebula-forms/conf.d/. These files will hold the configuration for our form handler.


Run the following command to create the conf.d folder.

mkdir /etc/nebula-forms/conf.d
mkdir /usr/local/etc/nebula-forms/conf.d

The Email Sender

The email plugin allows you to use an SMTP server or the sendmail program on the server to send emails. If you don't know what sendmail is or are using the Docker image, use SMTP.

Nebula needs to be configured to be able to connect to the SMTP server, or else it won't be able to send emails. This site uses SMTP through Nebula needs to be configured to be able to connect to the SMTP server, or else it won't be able to send emails. This site uses SMTP through [SparkPost][sparkpost], but the example below uses GMail's SMTP settings, since most people reading this guide probably have a Google account.

In the file /etc/nebula-forms/conf.d/gmail.toml, add the following, being sure to replace user, from, and pass with your full GMail address and password. You may need to use an app password to be able to connect, if you have two-factor authentication enabled.

    host = "smtp.gmail.com"
    port = 587
    user = "user@gmail.com"
    pass = "mygmailpassword"
    from = "user@gmail.com"

Save the file and exit the editor. You can find more about configuring SMTP senders here.

The Handler

Open the file /etc/nebula-forms/conf.d/contact.toml and add the following:

    allowed_origins = ["https://example.com"]
    path = "/contact"
    honeypot = "confirm_email"
    sender = "gmail"
    to = "admin@example.com"
    reply_to = """
    {{- $re := .Regexp.Email -}}
    {{- with (FormValue "email") -}}
        {{- if $re.MatchString . -}}
            {{- . -}}
        {{- else -}}
            {{- Errorf "%s doesn't look like an email address" . -}}
        {{- end -}}
    {{- else -}}
        {{- Errorf "An email address is required" -}}
    {{- end -}}
    subject = """
    {{- with (FormValue "summary") -}}
        {{- . -}}
    {{- else -}}
        {{- Errorf "Please summarize your reason for contacting" -}}
    {{- end -}}
    body = """
    {{- with (FormValue "explanation") -}}
        {{- . -}}
    {{- else -}}
        {{- Errorf "Please explain your reason for contacting" -}}
    {{- end }}

    Contact form submitted by {{ with (FormValue "name") -}}
        {{- . -}}
    {{- else -}}
        {{- Errorf "A name is required" -}}
    {{- end -}}

Save the file and exit.

There's a lot in this file, so we'll go through them one at a time.

allowed_origins defines which origins are allowed to submit forms to this handler. Think of an origin as a domain name with a protocol (http:// and https://). This helps prevent random people from sending junk to this handler from other websites.

path defines the path that this handler responds to. That helps differentiate this form from one at /other-contact or /survey. Multiple handlers can respond to the same path - they will either both handle the form submission, or they will be filtered out by conditions like allowed_origins.

honeypot defines which input field, if any, must be blank in order for the form to be handled. Honeypots are used to help protect against spam bots. The confirm_email field is hidden from view using CSS and is not visible to humans, so only bots - which just view the source code - will try to fill it out..

sender tells the handler to use the sender we previously configured.

to defines the email address to send the email to.

reply_to sets the “Reply-To” header of the email, which tells the email software to send replies to that address instead of the one that sent the original. This is set to a multi-line string (that's the triple quotation marks) that is a template that validates the email address before trying to use it.

In this case, the email address is validated in two ways. The first is based on whether there is any value, using the with statement, which runs if the value is not empty. The second uses a regular expression to see if the value matches the pattern of an email address. In both cases, an error is returned to the so they can fix the issue and try submitting again.

subject and body set the subject line and the body of the email, respectively. Both use the same pattern: check that the value is not empty and use it, or warn the user that it is empty.

Astute readers will notice that the form code above used the required attribute in all of the required inputs. Doesn't that mean that we don't need to validate it again in the handler?

Sadly, no. One of the main rules of web development is to not trust anything that comes from the client. In this case, they could have easily removed the required attributes from each input before submitting a blank form. Validating on the back-end prevents those submissions from going through.


That's it! If you start the server and submit the above form to the handler we just create, you should receive an email generated by Nebula Forms!

That said, you really should modify the configuration to fix with your own site, if you haven't already.