How I deploy to my server


I don't run this capsule on a server that I physically own. I rent out my server from digitalocean. Since my server doesn't need to have lots of computing power or memory, I have opted to use the cheapest server possible that pennies can buy. The only problem is that the server is so underpowered: it is nearly impossible to install software from source. In this log, I will explain how I go about deploying software to my server.

digitalocean.com

I've deployed my server as a droplet using a single shared CPU with 512 MB of memory. Because of these low specs, I have decided that all of my server/CGI software will run on native binaries to make using my capsule as smooth as possible.

The main server software that I use is made with Rust; a language known for its long and intensive compilations. I tried building it on my server and it took around 10 minutes before the build crashed from a lack of available memory. I would probably be better off cross compiling from my computer and uploading the binary than trying to find some workaround.

Uploading to the server

Compiling software for the server can vary widely from language to language, but it is almost always the same to upload your built binary to the server. The way I do it is by having a deploy.sh script in the root of a project that will build, upload, and install the software on the server.

BINARY=path/to/binary
REMOTE=root@my_server

BINAME="$(basename "$BINARY")"
BINDIR=/usr/local/bin
TMPDIR=/var/tmp

# TODO Build

scp "$BINARY" "$REMOTE:$TMPDIR/"
ssh "$REMOTE" "cd $TMPDIR && 
    install -Dm755 ./$BINAME $BINDIR/$BINAME &&
    rm ./$BINAME"

Of course depending on the type of software I'm deploying, the script may need to change to fit the project's needs. I feel that it would be simpler to just scp the binary directly into the installed destination rather than installing it with a separate command, but I kind of like it.

Deploying Rust

Rust has become my goto language when it comes to compiled software. It supports a lot of language features that I like. It can feel like a high-level language while still allowing for low level control. My problem with Rust is with how difficult it is to cross compile. In theory, it is very easy to cross compile Rust, but when external libraries are used: the process becomes infinitely more complicated.

You will first need to install a target for your toolchain (This is fairly easy). If the program doesn't use any external libraries, you should be good to go. Otherwise, you will need to install a C cross compiling toolchain (This can be tricky depending on your host system) and tell rust the linker you will be using in .cargo/config.toml. If your build still fails, a library might be compiling C from source, so you will need to tell rust that any C code should be built using your cross compiling toolchain in an environment variable. If you still can't cross compile the software, then get ready for a never ending rabbit hole.

There is a tool that will help you do cross compiling that I wish I had known about before I spent a week learning how to do it manually. It is called "Cross", and allows you to do cross compilations with very little hassle.

Cross

You will need to have Docker installed for Cross to work, but I think I have read that it might be possible to configure Cross to use Zig as a linker/compiler instead of building inside a Docker container.

Building with Cross is usually as simple as replacing cargo with cross. Depending on your needs, you may need to create a Cross.toml to customize your build, but that is still simpler than doing it manually.

TARGET=x86_64-unknown-linux-gnu
BINAME=<binary>
BINARY=target/release/$TARGET/$BINAME

cross build --release --target $TARGET

Deploying Go

In comparison to Rust, Go is a walk in the park. All that is needed to cross compile Go is two environment variables: GOOS and GOARCH. I don't know if there are any edge cases that might show up when building. I really should learn Go.

BINAME=<binary>
BINARY=$BINAME

GOOS=linux GOARCH=amd64 go build

I haven't needed to do anything else for Go to work, but I also don't know the language. If it were possible to use external libraries, then I would imagine that cross compilation could get more complicated, but I just don't know enough about the language to know if that's even possible.

Deploying my capsule

This isn't a language, but I figure it's worth mentioning. When I deploy my capsule, I upload the project up to the server and then render it. This allows for semi-dynamic content that is stored exclusively on the server to be rendered into the capsule. The way I go about deploying the capsule is a little bit funky, but it works and that's all that matters.

DIR="$(dirname -- "${BASH_SOURCE[0]}")"

SERVER='gemini@my_server'
DEST='/path/to/capsule/data/'
SOURCE="$DIR/."
GEMINI="/var/www/gemini/my.site/"

COMMAND="${1:-upload}"

# Upload to the server
if [ "$COMMAND" = "upload" ]; then
    # Synchronize the project with the server
    rsync -rltp --delete-after -z --exclude='./git' --filter=':- .gitignore' -h "$SOURCE" "$SERVER:$DEST" 
    echo "Uploaded capsule"

    # Run the build section of the deploy script on the server
    ssh -t "$SERVER" "cd $DEST ; ./deploy.sh build"
elif [ "$COMMAND" = "build" ]; then
    # Render the capsule
    cd "$DIR"
    kiln build
    RES=$?
    if [ $RES -ne 0 ]; then
        exit $RES
    fi

    # Copy the rendered files into the server folder
    rsync -r "${DEST}public/" "$GEMINI"
    RES=$?
    if [ $RES -ne 0 ]; then
        exit $RES
    fi
else
    echo "Invalid Command, should be one of [upload, build]"
fi

I haven't yet had the chance to deploy any other types of software, but I can imagine that some will be easier than others. Zig should be pretty simple, but C/C++ scares me. The thought of having to find or build libraries does not seem at all fun. Though C/C++ should still be easier than Rust.