Files for your game servers
Put config files and maps on disk in every game server of a fleet before the game starts, with one API call for each file.
What fleet files are#
A fleet file is a file you upload to a fleet. Every game server of the fleet has it on disk, at the path you chose, before your game process starts. Use them for what changes more often than your image: a server.cfg, a rotation list, a map that is too big or too new to bake in.
- Fleets only. Deploys do not take files.
- One set for the whole fleet. Every location gets the same files. There is no per-location or per-server file.
- Upload from the dashboard or the API. In the dashboard, open a fleet's actions menu and choose Files (see Use the dashboard), or call the API as below.
- A file can replace one in your image. A file at a path your image already has hides the image's file for the game, which is how you override a default config.
Use the dashboard#
On the Fleets page, open a fleet's actions menu and choose Files. The window lists the fleet's files with their path, size, a short SHA-256 and mode.
- Add or replace a file. Choose the file, type the path it goes to in the game server, and optionally a mode in octal, such as
644(the dashboard sends it as the decimal number the API takes). A bar shows the upload. A file already at that path is replaced. - Remove a file. Use the bin icon on its row and confirm.
- Every change replaces the fleet's servers. See When a file changes. The window says so before you upload.
- The dashboard checks the limits and path rules before it sends anything, and shows the API's reason when it refuses a file. While uploads are not enabled for the service it says so (the
409under Errors).
Upload a file with the API#
Send the file's bytes to the fleet with an API token. The path and the mode go in the query string, and the length and the SHA-256 of the file go in headers.
FILE=server.cfg
SHA=$(sha256sum "$FILE" | cut -d' ' -f1)
curl -X PUT "https://api.devlyft.io/v1/fleets/$FLEET_ID/files?path=/srv/cstrike/cfg/server.cfg&mode=420" \
-H "Authorization: Bearer $DEVLYFT_TOKEN" \
-H "Content-Type: application/octet-stream" \
-H "Content-Length: $(stat -c %s "$FILE")" \
-H "X-Content-SHA256: $SHA" \
--data-binary @"$FILE"
On macOS, use shasum -a 256 "$FILE" in place of sha256sum, and stat -f %z "$FILE" in place of stat -c %s. curl sends Content-Length for you when you send a file with --data-binary @file, so you can leave that header out.
| Part | What it is |
|---|---|
path (query, required) | Where the file goes in the game server's container: an absolute path, such as /srv/cstrike/cfg/server.cfg (see Paths). Uploading to a path that already has a file replaces it. |
mode (query, optional) | The permission bits as a decimal number, 0 to 511. 420 is 0644 and 438 is 0666. Left out, it is 0644 (see Ownership and modes). To convert, printf '%d\n' 0644 prints 420. |
Content-Length (required) | The size of the body in bytes. Send the size up front, not as a chunked upload. |
X-Content-SHA256 (required) | The SHA-256 of the body as 64 lower-case hex characters, which is what sha256sum prints. Nothing changes if the bytes you send do not hash to it, so a damaged upload never reaches a server. |
If-Match (optional) | The fleet's ETag, as for any other change. The upload is made only if the fleet is still at that version, and refused with a 412 if it is not. Send it when two people or scripts change the same fleet. Without it the file is added to the fleet as it is, so uploads to different paths at the same moment both land. |
{
"path": "/srv/cstrike/cfg/server.cfg",
"size": 2417,
"sha256": "9b74c9897bac770ffc029102a200c5de0c7e8e5e7c2b8b3b4d8f6f0a9a2c1d34",
"mode": 420,
"version": 7,
"rolls_servers": true
}
version is the fleet's version after the change: send it as If-Match on your next change. rolls_servers is true when the fleet's servers are being replaced to take the change (see When a file changes). Uploading exactly the same file again (same path, bytes and mode) changes nothing: the version stays, rolls_servers is false and no servers are replaced. A mode you left out comes back as 420.
An upload must finish within 30 minutes of the request starting, or it is cut off and nothing is changed.
A map goes up the same way, with a mode the game can read:
FILE=de_arena.bsp
curl -X PUT "https://api.devlyft.io/v1/fleets/$FLEET_ID/files?path=/srv/cstrike/maps/de_arena.bsp" \
-H "Authorization: Bearer $DEVLYFT_TOKEN" \
-H "Content-Type: application/octet-stream" \
-H "X-Content-SHA256: $(sha256sum "$FILE" | cut -d' ' -f1)" \
--data-binary @"$FILE"
Remove a file#
curl -X DELETE "https://api.devlyft.io/v1/fleets/$FLEET_ID/files?path=/srv/cstrike/cfg/server.cfg" \
-H "Authorization: Bearer $DEVLYFT_TOKEN" \
-H "If-Match: $ETAG"
The answer is the same shape as an upload's, describing the file that was removed and the fleet's new version. The fleet's servers are replaced onto the set without it, so a game that read the file from your image sees the image's version again. If-Match is optional here too. Removing a path the fleet has no file at is a 404.
Limits#
| Limit | Value |
|---|---|
| Files per fleet | 32 |
| Size of one file | 100 MiB |
| Size of all of a fleet's files | 500 MiB |
| Size of all files in your organisation | 5 GiB, added up over every fleet. Contact [email protected] if you need more. |
| Length of a path | 512 bytes |
A change that does not make your organisation's total larger, such as replacing a file with a smaller one or removing one, is never refused for the 5 GiB limit. The limit is checked when a file is recorded, so two uploads to different fleets at the same moment can together go slightly over it.
Paths#
A path must be an absolute, clean path to a file. It is refused when it:
- does not start with
/, or ends with/; - has a
..segment, a.segment or a doubled//(it must be unchanged by cleaning, so/srv/./a.cfgis refused); - is not valid UTF-8, has any control character (below
0x20, or0x7f, such as NUL, a newline or a tab), or is longer than 512 bytes; - is at or under
/proc,/sys,/dev,/runor/var/run, or is/etc/hosts,/etc/hostname,/etc/resolv.confor/etc/mtab: the platform owns these; - is a directory of another file of the fleet, such as
/srv/mapswhen the fleet already has/srv/maps/a.bsp(and the other way round).
Paths are case-sensitive, and each is one file: there is no way to upload a directory, so send each file of a map pack on its own.
Modes#
The mode may only have the permission bits 0777: no setuid, setgid or sticky bit. It must include the owner read bit (0400), so 0200 is refused. A mode of 0 means the default, 0644. Two paths that hold the same content must have the same mode (a mode of 0 counts as 0644); otherwise the upload is refused with a 400.
When a file changes#
Servers read their files once, as they start. So adding, replacing or removing a file replaces the fleet's servers, the way a new build does:
- Ready servers are replaced first, in steps. Servers are replaced in steps of up to a quarter of the fleet at a time, and the fleet may run up to a quarter more servers than its size while it does.
- A server in a match is left alone until the match ends. It keeps the files it started with, and the game it is running keeps running. The next server that takes its place has the new files.
Until a fleet has finished rolling, an allocation can be given a server with the old files or the new ones.
Ownership and modes#
Your files are put on disk by the platform, as a user that is not root. They belong to that user, not to the user your game runs as.
- A game that starts as root can read every file, whatever its mode. Nothing to do.
- A game that runs as another non-root user, such as the
steamuser in many Source-engine images, reads a file only through its other bits. The default0644allows that. A mode such as0600or0640does not, and the game fails to read the file. - If the game has to write the file, such as a config it saves, give it
0666(mode=438), so a user that is not the owner can write.
Inputs, not storage#
A file is writable inside the running server, if its mode allows it. But a change the game makes is lost when that server is replaced: whenever a server is replaced: after you change a file or roll the fleet onto a new build, or when an unhealthy server is replaced. A new server starts again from the files you uploaded. Treat fleet files as inputs. Keep what your game must keep, such as saves or player data, somewhere that outlives the server.
Files count towards the server's disk. A fleet with 500 MiB of files needs about that much room on each server, on top of what the game uses.
Errors#
Errors are problem details; show detail, and branch on status. Nothing is changed when a request fails, except where noted.
| Answer | Means | Do |
|---|---|---|
400 | The request is not valid. The detail says which part: a path or mode that breaks the rules above, an X-Content-SHA256 that is not 64 lower-case hex characters, a body that does not hash to it, a body that is not Content-Length bytes long, a malformed If-Match, or a fleet that was made without a build. | Fix the request. After a hash or length mismatch, work out the hash and size again from the file you send, and check that nothing changes the file in between. For a fleet without a build, roll it onto one first. |
402 | Your organisation has not finished billing setup. | Add a card under Billing. This is checked on an upload only; removing a file does not need it. |
403 | The upload would take your organisation over its 5 GiB limit. The body has reason: quota_exceeded with the limit and the size it requested. | Remove files you no longer use from your fleets, or ask for a higher limit. |
404 | No such fleet, or (on a delete) no file at that path. | Check the fleet id and the path. |
409 | Either file uploads are not enabled for the service yet, or the fleet is being deleted. While uploads are not enabled, updating or rolling a fleet that already has files is also refused with a 409 that tells you to remove the files first. | For the first, the same request works once uploads are on: contact [email protected]. Removing a file keeps working while uploads are off, so you can remove the files and then update or roll the fleet. For the second, create a new fleet. |
412 | The fleet changed since the version you named in If-Match. Nothing was changed. | Read the fleet again for its current ETag, and decide if your change still makes sense. |
413 | The file is over 100 MiB, or the fleet would have more than 32 files, or more than 500 MiB of files. | Shrink the file or remove another one. A file over 100 MiB is refused before its bytes are read. |
429 | Too many uploads are being received at once. Nothing was read or changed. The body has retry_after_seconds (5). | Wait that many seconds and send the same request again. |
A 500 can leave a file's bytes stored but not recorded on the fleet. That costs only space, and uploading the same file again reuses them.
When a server cannot get its files#
A game server fetches its files before the game starts, and retries for about ten minutes. If it still cannot get them, or what it got does not match the SHA-256 you declared, the server does not start. The game never runs without its files.
- The new game server shows Error. The platform keeps it for a while and then replaces it, and the replacement fails the same way, so a rollout with a file that cannot be fetched keeps replacing servers without any of them becoming Ready.
- The old servers keep serving until the new ones are Ready, and servers in a match are never touched.
- Nothing from your game appears in Watch logs for a failed server, because the game did not start.
- Check the paths and modes you uploaded, then upload the file again, or remove it.
If servers keep showing Error for longer than ten minutes after a change, contact [email protected] with the fleet id.
Security#
- Files are stored encrypted at rest, and are kept per organisation: no other organisation can reach them.
- Each server is given a short-lived link to fetch its files, only before the game starts. The game process never sees the link.
- The API never shows a file's bytes back. Reading the fleet returns each file's metadata (path, size, SHA-256 and mode), and a change answers with the same, but there is no download.
- A file is on the disk of the machine that runs the server, and readable by your game and by anything in your game's container. Do not put secrets in files when they can be secrets in the environment: a database password belongs in a secret, not in
server.cfg. If a config needs one, have the game read it from the environment when it starts.
Not available yet#
- Downloading a file back. Keep your own copy.
- Files for deploys. Only fleets take files.
- Different files in different locations, or per server.